blob: b42f5580fb29aa03153e9ac47c072cd4b58da604 [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
Jose Pascoal4fe7ba52015-03-13 16:54:31 +000019import android.app.DownloadManager;
Jose Pascoal61e24762014-09-30 19:44:36 +010020import android.content.Context;
Jose Pascoale4c48e32014-10-03 19:55:39 +010021import android.content.Intent;
Maarten Derkse2f0ffc2016-04-26 11:58:36 +020022import android.content.IntentFilter;
Jose Pascoal0a5be012014-11-17 16:55:40 +000023import android.content.res.Resources;
Jose Pascoal2a8283b2015-03-13 14:43:42 +000024import android.database.Cursor;
Filipe Gonçalves72817782015-03-19 15:29:05 +000025import android.net.ConnectivityManager;
Jose Pascoal2a8283b2015-03-13 14:43:42 +000026import android.net.Uri;
Maarten Derkse2f0ffc2016-04-26 11:58:36 +020027import android.os.BatteryManager;
Jose Pascoal54b3ae62014-10-07 20:29:58 +010028import android.os.Build;
Jose Pascoal0a5be012014-11-17 16:55:40 +000029import android.os.Environment;
Jose Pascoal49f058a2015-02-13 16:58:01 +000030import android.os.PowerManager;
Jose Pascoalda00b382015-01-20 18:39:28 +000031import android.text.TextUtils;
Jose Pascoal02b849e2014-06-26 17:07:51 +010032import android.util.Log;
Jose Pascoal49f058a2015-02-13 16:58:01 +000033import android.widget.Toast;
Jose Pascoal02b849e2014-06-26 17:07:51 +010034
Jose Pascoal46fdb062015-02-05 18:59:32 +000035import com.fairphone.updater.BetaEnabler;
Jose Pascoal0a5be012014-11-17 16:55:40 +000036import com.fairphone.updater.R;
Jose Pascoale4c48e32014-10-03 19:55:39 +010037import com.fairphone.updater.UpdaterService;
Jose Pascoal02d86242014-12-17 18:50:08 +000038import com.fairphone.updater.data.DownloadableItem;
39import com.fairphone.updater.data.Store;
Jose Pascoal055562d2015-03-19 19:31:38 +000040import com.fairphone.updater.data.UpdaterData;
Jose Pascoal7bf83a02014-10-13 18:30:18 +010041import com.fairphone.updater.data.Version;
42import com.fairphone.updater.data.VersionParserHelper;
Filipe Gonçalves837e1572015-01-23 17:52:35 +000043import com.stericson.RootTools.RootTools;
44import com.stericson.RootTools.exceptions.RootDeniedException;
45import com.stericson.RootTools.execution.CommandCapture;
46import com.stericson.RootTools.execution.Shell;
Jose Pascoal61e24762014-09-30 19:44:36 +010047
Tiago Costad7e951f2015-03-02 17:37:33 +000048import java.io.DataOutputStream;
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +000049import java.io.File;
50import java.io.FileInputStream;
51import java.io.FileNotFoundException;
Filipe Gonçalves49ce23c2015-02-13 16:33:52 +000052import java.io.FileOutputStream;
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +000053import java.io.IOException;
54import java.io.InputStream;
Jose Pascoal49f058a2015-02-13 16:58:01 +000055import java.io.PrintWriter;
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +000056import java.math.BigInteger;
Filipe Gonçalves49ce23c2015-02-13 16:33:52 +000057import java.nio.channels.FileChannel;
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +000058import java.security.MessageDigest;
59import java.security.NoSuchAlgorithmException;
Filipe72b5a7f2015-09-01 08:59:54 +000060import java.util.HashMap;
61import java.util.Map;
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +000062import java.util.NoSuchElementException;
63import java.util.Scanner;
64import java.util.concurrent.TimeoutException;
Filipe72b5a7f2015-09-01 08:59:54 +000065import java.util.regex.Matcher;
66import java.util.regex.Pattern;
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +000067
Jose Pascoal810950b2014-10-09 17:16:08 +010068public class Utils
69{
Jose Pascoal810950b2014-10-09 17:16:08 +010070 private static final String TAG = Utils.class.getSimpleName();
Jose Pascoal46fdb062015-02-05 18:59:32 +000071 private static final int DELAY_100_MILLIS = 100;
72 public static final int DELAY_HALF_SECOND = 500;
Jose Pascoalcfc2dd42015-02-09 18:00:05 +000073 public static final long SECONDS_IN_MINUTE = 60L;
74 public static final long MINUTES_IN_HOUR = 60L;
Jose Pascoal46fdb062015-02-05 18:59:32 +000075
76 private static final double BUFFER_1024_BYTES = 1024d;
Jose Pascoalcdd82042015-02-06 13:04:26 +000077 // --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 +000078 public static final int BUFFER_SIZE_2_KBYTES = 2048;
79 private static final int BUFFER_SIZE_8_KBYTES = 8192;
80 public static final int BUFFER_SIZE_10_MBYTES = 10240;
81 private static final int RADIX_BASE_16 = 16;
82 private static final double PERCENT_100 = 100d;
Jose Pascoalcfc2dd42015-02-09 18:00:05 +000083 private static final char CHAR_SPACE = ' ';
84 private static final char CHAR_ZERO = '0';
Maarten Derksbb287762015-10-12 17:22:45 +020085 public static final String GAPPS_STORE_NUMBER = "0";
Jose Pascoal02b849e2014-06-26 17:07:51 +010086
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +000087 private static double getPartitionSizeInGBytes(File path)
Jose Pascoal810950b2014-10-09 17:16:08 +010088 {
89 double availableBlocks = getPartitionSizeInBytes(path);
Jose Pascoal46fdb062015-02-05 18:59:32 +000090 double sizeInGB = ((availableBlocks / BUFFER_1024_BYTES) / BUFFER_1024_BYTES) / BUFFER_1024_BYTES;
Jose Pascoal810950b2014-10-09 17:16:08 +010091 Log.d(TAG, path.getPath() + " size(GB): " + sizeInGB);
92 return sizeInGB;
93 }
Jose Pascoal02b849e2014-06-26 17:07:51 +010094
Jose Pascoalcdd82042015-02-06 13:04:26 +000095// --Commented out by Inspection START (06/02/2015 12:26):
96// public static double getPartitionSizeInMBytes(File path)
97// {
98// double availableBlocks = getPartitionSizeInBytes(path);
99// double sizeInMB = ((availableBlocks / BUFFER_1024_BYTES)) / BUFFER_1024_BYTES;
100// return sizeInMB;
101// }
102// --Commented out by Inspection STOP (06/02/2015 12:26)
Jose Pascoale4c48e32014-10-03 19:55:39 +0100103
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000104 private static long getPartitionSizeInBytes(File path)
Jose Pascoal810950b2014-10-09 17:16:08 +0100105 {
106 android.os.StatFs stat = new android.os.StatFs(path.getPath());
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000107 long blockSize, blockCount;
Jose Pascoal46fdb062015-02-05 18:59:32 +0000108 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000109 blockSize = stat.getBlockSizeLong();
110 blockCount = stat.getBlockCountLong();
111 } else {
112 // deprectation warnings disabled due to the need to support SDK 17 (FP1)
113 //noinspection deprecation
114 blockSize = stat.getBlockSize();
115 //noinspection deprecation
116 blockCount = stat.getBlockCount();
117 }
118 return blockCount * blockSize;
Jose Pascoal810950b2014-10-09 17:16:08 +0100119 }
Jose Pascoale4c48e32014-10-03 19:55:39 +0100120
Jose Pascoal810950b2014-10-09 17:16:08 +0100121 public static long getAvailablePartitionSizeInBytes(File path)
122 {
123 android.os.StatFs stat = new android.os.StatFs(path.getPath());
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000124
125 long blockSize, blockCount;
Jose Pascoal46fdb062015-02-05 18:59:32 +0000126 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000127 blockSize = stat.getBlockSizeLong();
128 blockCount = stat.getAvailableBlocksLong();
129 } else {
130 // deprectation warnings disabled due to the need to support SDK 17 (FP1)
131 //noinspection deprecation
132 blockSize = stat.getBlockSize();
133 //noinspection deprecation
134 blockCount = stat.getAvailableBlocks();
135 }
136 return blockCount * blockSize;
Jose Pascoal810950b2014-10-09 17:16:08 +0100137 }
Jose Pascoale4c48e32014-10-03 19:55:39 +0100138
Jose Pascoal810950b2014-10-09 17:16:08 +0100139 public static void startUpdaterService(Context context, boolean forceDownload)
140 {
Filipe72b5a7f2015-09-01 08:59:54 +0000141 Intent i = new Intent(context, UpdaterService.class);
142 i.putExtra(UpdaterService.EXTRA_FORCE_CONFIG_FILE_DOWNLOAD, forceDownload);
143 context.startService(i);
Jose Pascoal810950b2014-10-09 17:16:08 +0100144 }
Jose Pascoale4c48e32014-10-03 19:55:39 +0100145
Jose Pascoalcdd82042015-02-06 13:04:26 +0000146// --Commented out by Inspection START (06/02/2015 12:26):
147// public static void stopUpdaterService(Context context)
148// {
149// boolean isRunning = isServiceRunning(context);
150//
151// if (isRunning)
152// {
153// Log.i(TAG, "Stoping Updater Service...");
154// Intent i = new Intent(context, UpdaterService.class);
155// context.stopService(i);
156// try
157// {
158// Thread.sleep(DELAY_100_MILLIS * 2);
159// } catch (InterruptedException e)
160// {
161// Log.w(TAG, "Stop Updater service delay error: " + e.getLocalizedMessage());
162// }
163// }
164// }
165// --Commented out by Inspection STOP (06/02/2015 12:26)
Jose Pascoale4c48e32014-10-03 19:55:39 +0100166
Jose Pascoal810950b2014-10-09 17:16:08 +0100167 // **************************************************************************************************************
168 // HELPERS
169 // **************************************************************************************************************
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100170
Jose Pascoal810950b2014-10-09 17:16:08 +0100171 public static boolean checkMD5(String md5, File updateFile)
172 {
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100173
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000174 if (updateFile == null || !updateFile.exists())
Jose Pascoal810950b2014-10-09 17:16:08 +0100175 {
176 return false;
177 }
178
Jose Pascoalcfc2dd42015-02-09 18:00:05 +0000179 if (md5 == null || md5.isEmpty())
Jose Pascoal810950b2014-10-09 17:16:08 +0100180 {
181 Log.e(TAG, "MD5 String NULL or UpdateFile NULL");
182 return false;
183 }
184
185 String calculatedDigest = calculateMD5(updateFile);
186 if (calculatedDigest == null)
187 {
188 Log.e(TAG, "calculatedDigest NULL");
189 return false;
190 }
191
192 return calculatedDigest.equalsIgnoreCase(md5);
193 }
194
Filipe72b5a7f2015-09-01 08:59:54 +0000195 public static String calculateMD5(File updateFile)
Jose Pascoal810950b2014-10-09 17:16:08 +0100196 {
197 MessageDigest digest;
198 try
199 {
200 digest = MessageDigest.getInstance("MD5");
201 } catch (NoSuchAlgorithmException e)
202 {
203 Log.e(TAG, "Exception while getting Digest", e);
204 return null;
205 }
206
207 InputStream is;
208 try
209 {
210 is = new FileInputStream(updateFile);
211 } catch (FileNotFoundException e)
212 {
213 Log.e(TAG, "Exception while getting FileInputStream", e);
214 return null;
215 }
216
Jose Pascoal46fdb062015-02-05 18:59:32 +0000217 byte[] buffer = new byte[BUFFER_SIZE_8_KBYTES];
Jose Pascoal810950b2014-10-09 17:16:08 +0100218 int read;
219 try
220 {
221 while ((read = is.read(buffer)) > 0)
222 {
223 digest.update(buffer, 0, read);
224 }
225 byte[] md5sum = digest.digest();
226 BigInteger bigInt = new BigInteger(1, md5sum);
Jose Pascoal46fdb062015-02-05 18:59:32 +0000227 String output = bigInt.toString(RADIX_BASE_16);
Jose Pascoal810950b2014-10-09 17:16:08 +0100228 // Fill to 32 chars
Jose Pascoalcfc2dd42015-02-09 18:00:05 +0000229 output = String.format("%32s", output).replace(CHAR_SPACE, CHAR_ZERO);
Jose Pascoal810950b2014-10-09 17:16:08 +0100230 return output;
231 } catch (IOException e)
232 {
Jose Pascoalcfc2dd42015-02-09 18:00:05 +0000233 Log.e(TAG, "Error digesting MD5: " + e.getLocalizedMessage());
Tiago Costa7c47e2d2014-11-18 16:50:35 +0000234 return null;
235// throw new RuntimeException("Unable to process file for MD5", e);
Jose Pascoal810950b2014-10-09 17:16:08 +0100236 } finally
237 {
238 try
239 {
240 is.close();
241 } catch (IOException e)
242 {
243 Log.e(TAG, "Exception on closing MD5 input stream", e);
244 }
245 }
246 }
247
Filipe Gonçalves49ce23c2015-02-13 16:33:52 +0000248 public static void copy(File src, File dst) throws IOException {
249 if (PrivilegeChecker.isPrivilegedApp()) {
250 copyPrivileged(src, dst);
251 } else {
252 copyUnprivileged(src, dst);
253 }
254 }
255
Filipe Gonçalves2ba0f552015-02-16 12:53:15 +0000256 private static void copyUnprivileged(File src, File dst) throws IOException {
Filipe Gonçalves49ce23c2015-02-13 16:33:52 +0000257 if (RootTools.isAccessGiven()) {
258 RootTools.copyFile(src.getPath(), dst.getPath(), false, false);
259 } else {
260 throw new IOException("No root permissions granted.");
261 }
262 }
263
Filipe Gonçalves2ba0f552015-02-16 12:53:15 +0000264 private static void copyPrivileged(File src, File dst) throws IOException {
Filipe Gonçalves49ce23c2015-02-13 16:33:52 +0000265 FileInputStream inStream = new FileInputStream(src);
266 FileOutputStream outStream = new FileOutputStream(dst);
267 FileChannel inChannel = inStream.getChannel();
268 FileChannel outChannel = outStream.getChannel();
269 inChannel.transferTo(0, inChannel.size(), outChannel);
270 inStream.close();
271 outStream.close();
272 }
273
Jose Pascoal0a5be012014-11-17 16:55:40 +0000274 public static void clearCache()
275 {
Jose Pascoal288dd2f2015-02-20 19:19:04 +0000276 if(PrivilegeChecker.isPrivilegedApp()) {
277 File f = Environment.getDownloadCacheDirectory();
278 File[] files = f.listFiles();
279 if (files != null) {
280 Log.d(TAG, "Size: " + files.length);
281 for (File file : files) {
282 String filename = file.getName();
Jose Pascoal0a5be012014-11-17 16:55:40 +0000283
Jose Pascoal288dd2f2015-02-20 19:19:04 +0000284 if (filename.endsWith(".zip")) {
285 final boolean delete = file.delete();
286 if (delete) {
287 Log.d(TAG, "Deleted file " + filename);
288 } else {
289 Log.d(TAG, "Failed to delete file " + filename);
290 }
291 }
292 }
293 }
294 } else {
295 if(RootTools.isAccessGiven()) {
296 try {
297 Shell.runRootCommand(new CommandCapture(0, "rm -f *.zip"));
298 } catch (IOException | TimeoutException |RootDeniedException e) {
299 Log.w(TAG, "Failed to clear cache: " + e.getLocalizedMessage());
300 }
301 }
Jose Pascoal0a5be012014-11-17 16:55:40 +0000302 }
303 }
304
305 public static boolean hasUnifiedPartition(Resources resources)
306 {
307 File path = Environment.getDataDirectory();
308 double sizeInGB = Utils.getPartitionSizeInGBytes(path);
Jose Pascoal46fdb062015-02-05 18:59:32 +0000309 double roundedSize = Math.ceil(sizeInGB * PERCENT_100) / PERCENT_100;
Jose Pascoal0a5be012014-11-17 16:55:40 +0000310 Log.d(TAG, "/data size: " + roundedSize + "Gb");
311
Jose Pascoal8d4458f2015-03-17 20:03:59 +0000312 double fp1DataPartitionSize = (double) resources.getInteger(R.integer.FP1DataPartitionSizeMb) / BUFFER_1024_BYTES;
Jose Pascoal0a5be012014-11-17 16:55:40 +0000313 // Add a little buffer to the 1gb default just in case
314 return roundedSize > fp1DataPartitionSize;
315 }
316
317 public static String getPartitionDownloadPath(Resources resources)
318 {
319 String downloadPath = "";
320 if (Build.MODEL.equals(resources.getString(R.string.FP1Model)))
321 {
322 downloadPath =
323 Utils.hasUnifiedPartition(resources) ? resources.getString(R.string.unifiedDataPartition) : resources
324 .getString(R.string.oneGBDataPartition);
325 }
326 return downloadPath;
327 }
328
329 public static boolean canCopyToCache(File file)
330 {
331 double fileSize = file.length();
332 double cacheSize = Utils.getPartitionSizeInBytes(Environment.getDownloadCacheDirectory());
Jose Pascoalb980c862015-03-12 20:22:02 +0000333 return fileSize > 0 && cacheSize >= fileSize;
Jose Pascoal0a5be012014-11-17 16:55:40 +0000334 }
Jose Pascoal40916302015-02-06 18:43:47 +0000335
336 public static String getFilenameFromDownloadableItem(DownloadableItem item, boolean isVersion)
Jose Pascoal02d86242014-12-17 18:50:08 +0000337 {
Jose Pascoalcfc2dd42015-02-09 18:00:05 +0000338 StringBuilder filename;
Jose Pascoal40916302015-02-06 18:43:47 +0000339
340 if(isVersion)
341 {
342 filename = getFilenameForItem(item, "update_");
343 }
344 else
345 {
346 filename = getFilenameForItem(item, "store_");
347 }
348
349 return filename.toString();
350 }
351
352 private static StringBuilder getFilenameForItem(DownloadableItem item, String type) {
Jose Pascoal02d86242014-12-17 18:50:08 +0000353 StringBuilder filename = new StringBuilder();
354 filename.append("fp_");
355 if (item != null)
356 {
Jose Pascoal40916302015-02-06 18:43:47 +0000357 filename.append(type);
Jose Pascoal02d86242014-12-17 18:50:08 +0000358 }
359 filename.append(".zip");
Jose Pascoal40916302015-02-06 18:43:47 +0000360 return filename;
Jose Pascoal02d86242014-12-17 18:50:08 +0000361 }
362
Jose Pascoal40916302015-02-06 18:43:47 +0000363 public static String getDownloadTitleFromDownloadableItem(Resources resources, DownloadableItem item, boolean isVersion){
Jose Pascoal02d86242014-12-17 18:50:08 +0000364 String title = "";
365 if (item != null)
366 {
Jose Pascoal40916302015-02-06 18:43:47 +0000367 if (isVersion)
Jose Pascoal02d86242014-12-17 18:50:08 +0000368 {
369 Version version = (Version) item;
Maarten Derks1b51ea52016-03-08 09:02:14 +0100370 title = version.getHumanReadableName();
Jose Pascoal02d86242014-12-17 18:50:08 +0000371 }
Jose Pascoal40916302015-02-06 18:43:47 +0000372 else
Jose Pascoal02d86242014-12-17 18:50:08 +0000373 {
374 Store store = (Store) item;
375 title = store.getName();
376 }
377 }
378 return title;
379 }
Jose Pascoalda00b382015-01-20 18:39:28 +0000380
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000381 public static boolean isDeviceUnsupported(Context context)
Jose Pascoalda00b382015-01-20 18:39:28 +0000382 {
383 Version deviceVersion = VersionParserHelper.getDeviceVersion(context);
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000384 return deviceVersion == null || TextUtils.isEmpty(deviceVersion.getName());
Jose Pascoalda00b382015-01-20 18:39:28 +0000385 }
Filipe Gonçalves837e1572015-01-23 17:52:35 +0000386
Filipe72b5a7f2015-09-01 08:59:54 +0000387 private static Map<String,String> buildProps;
388 public static Map<String,String> getpropAll(){
389
390 if(buildProps==null) {
391 buildProps = new HashMap<>();
392 ProcessBuilder pb = new ProcessBuilder("/system/bin/getprop");
393 pb.redirectErrorStream(true);
394 Pattern propRegex = Pattern.compile("\\[([^\\]]+)\\]: \\[([^\\]]+)\\]");
395
396 Process p;
397 InputStream is = null;
398 try {
399 p = pb.start();
400 is = p.getInputStream();
401 Scanner scan = new Scanner(is);
402 String prop;
403 do {
404 prop = scan.nextLine();
405 Matcher match = propRegex.matcher(prop);
406 if(match.find()){
407 buildProps.put(match.group(1), match.group(2));
408 }
409 } while(!prop.isEmpty());
410 } catch (NoSuchElementException e) {
411 } catch (IOException e) {
Filipe Gonçalves837e1572015-01-23 17:52:35 +0000412 }
413 }
Filipe72b5a7f2015-09-01 08:59:54 +0000414 return buildProps;
Filipe Gonçalves837e1572015-01-23 17:52:35 +0000415 }
Filipe Gonçalves49ce23c2015-02-13 16:33:52 +0000416
Filipe72b5a7f2015-09-01 08:59:54 +0000417 public static String getprop(String name, String defaultValue)
418 {
419 String result;
420 if (getpropAll().containsKey(name)){
421 result = getpropAll().get(name);
422 } else {
423 result = defaultValue;
424 }
425 return result;
426 }
Filipe Gonçalves49ce23c2015-02-13 16:33:52 +0000427
Filipe72b5a7f2015-09-01 08:59:54 +0000428 public static void setBetaPropToEnable()
Filipe Gonçalves837e1572015-01-23 17:52:35 +0000429 {
430 if(RootTools.isAccessGiven()) {
Filipe72b5a7f2015-09-01 08:59:54 +0000431 CommandCapture command = new CommandCapture(0, "/system/bin/setprop "+ BetaEnabler.FAIRPHONE_BETA_PROPERTY+" "+BetaEnabler.BETA_ENABLED);
Filipe Gonçalves837e1572015-01-23 17:52:35 +0000432 try {
433 Shell.runRootCommand(command);
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000434 } catch (IOException | TimeoutException | RootDeniedException e) {
435 Log.d(TAG, "Failed to setprop: " + e.getLocalizedMessage());
Filipe Gonçalves837e1572015-01-23 17:52:35 +0000436 }
437 }
Filipe72b5a7f2015-09-01 08:59:54 +0000438 try {
439 Thread.sleep(200);
440 } catch (InterruptedException e) {
441
442 }
443 buildProps = null;
Filipe Gonçalves837e1572015-01-23 17:52:35 +0000444 }
Jose Pascoal49f058a2015-02-13 16:58:01 +0000445
Jose Pascoal60c99372015-03-17 15:52:59 +0000446 public static String getOtaPackagePath(Resources resources, DownloadableItem item, boolean isVersion, boolean isZipInstall){
Jose Pascoal49f058a2015-02-13 16:58:01 +0000447 String path;
448
449 if (Utils.hasUnifiedPartition(resources))
450 {
451 path = resources.getString(R.string.recoveryCachePath) + Utils.getFilenameFromDownloadableItem(item, isVersion);
452 }
453 else
454 {
Jose Pascoal60c99372015-03-17 15:52:59 +0000455 if(isZipInstall && Build.MODEL.equalsIgnoreCase(resources.getString(R.string.FP1Model)))
456 {
457 //TODO: Find a way to not have this hardcoded
458 String zipPath = item.getDownloadLink();
459 path = zipPath.replace("/storage/sdcard0", resources.getString(R.string.recoverySdCardPath));
460 }
461 else
462 {
463 path = resources.getString(R.string.recoverySdCardPath) + resources.getString(R.string.updaterFolder) + Utils.getFilenameFromDownloadableItem(item, isVersion);
464 }
Jose Pascoal49f058a2015-02-13 16:58:01 +0000465 }
466
467 return path;
468 }
469
470 public static void writeCacheCommand(Context context, String otaPackagePath) throws IOException, TimeoutException, RootDeniedException, Resources.NotFoundException {
471 if (PrivilegeChecker.isPrivilegedApp()) {
472 File recovery_dir = new File("/cache/recovery/");
473 final boolean mkdirs = recovery_dir.mkdirs();
Filipe Gonçalves2ba0f552015-02-16 12:53:15 +0000474 if(! (mkdirs || recovery_dir.exists()) ) {
Jose Pascoal49f058a2015-02-13 16:58:01 +0000475 String errorMessage = context.getResources().getString(R.string.failed_mkdirs_cache_message);
476 Toast.makeText(context, errorMessage, Toast.LENGTH_LONG).show();
477 throw new IOException(errorMessage);
478 }
479
480 File command = new File("/cache/recovery/command");
481 File extendedCommand = new File("/cache/recovery/extendedcommand");
Filipe Gonçalves2ba0f552015-02-16 12:53:15 +0000482 final boolean deleteFailed = !extendedCommand.delete();
483 if (deleteFailed) {
Jose Pascoal49f058a2015-02-13 16:58:01 +0000484 Log.d(TAG, "Couldn't delete "+extendedCommand.getAbsolutePath());
485 }
486
487 String updateCommand = "--update_package=" + otaPackagePath;
488 PrintWriter writer = new PrintWriter(command, "UTF-8");
489 writer.println("--wipe_cache");
490 writer.println(updateCommand);
491 writer.flush();
492 writer.close();
493 }else {
494 if(RootTools.isAccessGiven()) {
495 Shell.runRootCommand(new CommandCapture(0, "rm -f /cache/recovery/command"));
496 Shell.runRootCommand(new CommandCapture(0, "rm -f /cache/recovery/extendedcommand"));
497 Shell.runRootCommand(new CommandCapture(0, "echo '--wipe_cache' >> /cache/recovery/command"));
498 Shell.runRootCommand(new CommandCapture(0, "echo '--update_package=" + otaPackagePath + "' >> /cache/recovery/command"));
499 }else{
500 throw new RootDeniedException("Root Denied");
501 }
502 }
503 }
504
Filipe Gonçalves2ba0f552015-02-16 12:53:15 +0000505 @SuppressWarnings("BooleanMethodIsAlwaysInverted")
Jose Pascoal49f058a2015-02-13 16:58:01 +0000506 public static boolean rebootToRecovery(Context context) {
507 boolean result;
508 if (PrivilegeChecker.isPrivilegedApp()) {
509 ((PowerManager) context.getSystemService(Context.POWER_SERVICE)).reboot("recovery");
510 result = false;
511 } else {
512 if(RootTools.isAccessGiven()) {
513 try {
514 Shell.runRootCommand(new CommandCapture(0, "reboot recovery"));
515 result = true;
516 } catch (IOException | TimeoutException | RootDeniedException e) {
517 Log.e(TAG, "Error rebooting to recovery: " + e.getLocalizedMessage());
518 result = false;
519 }
520 }else{
521 result = false;
522 }
523
524 }
525 return result;
526 }
Jose Pascoal87758742015-01-28 20:00:22 +0000527
Jose Pascoalcdd82042015-02-06 13:04:26 +0000528// --Commented out by Inspection START (06/02/2015 12:25):
529// public static void printStack(String moreLogs)
530// {
531// StringBuilder sb = new StringBuilder(moreLogs);
532// sb.append("\nStack --> ");
533// for (StackTraceElement ste : Thread.currentThread().getStackTrace())
534// {
535// sb.append(ste.getFileName()).append(" : ").append(ste.getMethodName()).append(":").append(ste.getLineNumber()).append(" -- ");
536// }
537// Log.wtf(TAG, sb.toString());
538// }
539// --Commented out by Inspection STOP (06/02/2015 12:25)
Jose Pascoal288dd2f2015-02-20 19:19:04 +0000540
541 public static boolean fileExists(String otaPackagePath) {
542 boolean fileExists;
543 if(PrivilegeChecker.isPrivilegedApp()){
544 File f = new File(otaPackagePath);
545 fileExists = f.exists();
546 }else {
547 fileExists = RootTools.exists(otaPackagePath);
548 }
549 return fileExists;
550 }
Tiago Costad7e951f2015-03-02 17:37:33 +0000551
Tiago Costa3d30c7b2015-03-03 16:11:52 +0000552 private final static String[] SHELL_COMMANDS_ERASE_DATA = {
Tiago Costad7e951f2015-03-02 17:37:33 +0000553 // remove data
554 "rm -rf /data/data/com.android.providers.media*",
555 "rm -rf /data/data/com.android.keychain*",
556 "rm -rf /data/data/com.android.location.fused*",
557 "rm -rf /data/data/com.android.providers.applications*",
558 "rm -rf /data/data/com.android.providers.media*",
559 "rm -rf /data/data/com.android.vending*",
560 "rm -rf /data/data/com.google.android.apps.genie.geniewidget*",
561 "rm -rf /data/data/com.google.android.apps.plus*",
562 "rm -rf /data/data/com.google.android.ears*",
563 "rm -rf /data/data/com.google.android.gms*",
564 "rm -rf /data/data/com.google.android.googlequicksearchbox*",
565 "rm -rf /data/data/com.google.android.location*",
566 "rm -rf /data/data/com.google.android.marvin.talkback*",
567 // remove cache
Tiago Costa3d30c7b2015-03-03 16:11:52 +0000568// "rm -rf /data/dalvik-cache",
Tiago Costad7e951f2015-03-02 17:37:33 +0000569 // remove data/app
570 "rm -rf /data/app/com.android.apps.plus*",
571 "rm -rf /data/app/com.android.vending*",
572 "rm -rf /data/app/com.android.easr*",
573 "rm -rf /data/app/com.android.gms*",
574 "rm -rf /data/app/com.android.tts*"
575 };
576
Tiago Costa3d30c7b2015-03-03 16:11:52 +0000577 public final static String SHELL_COMMAND_ERASE_DALVIK_CACHE = "rm -rf /data/dalvik-cache";
Tiago Costad7e951f2015-03-02 17:37:33 +0000578 public final static String SHELL_COMMAND_EXIT = "exit";
579
580 public static void clearGappsData() throws RootDeniedException, IOException, InterruptedException {
581
582 if (PrivilegeChecker.isPrivilegedApp()) {
Tiago Costa3d30c7b2015-03-03 16:11:52 +0000583 Process p = Runtime.getRuntime().exec(SHELL_COMMAND_ERASE_DALVIK_CACHE);
Tiago Costad7e951f2015-03-02 17:37:33 +0000584 DataOutputStream os = new DataOutputStream(p.getOutputStream());
Tiago Costa3d30c7b2015-03-03 16:11:52 +0000585 for (String tmpCmd : SHELL_COMMANDS_ERASE_DATA) {
Tiago Costad7e951f2015-03-02 17:37:33 +0000586 os.writeBytes(tmpCmd+"\n");
587 }
588 os.writeBytes(SHELL_COMMAND_EXIT+"\n");
589 os.flush();
590 p.waitFor();
591 }else {
592 if(RootTools.isAccessGiven()) {
Tiago Costa3d30c7b2015-03-03 16:11:52 +0000593 try {
594 Shell.runRootCommand(new CommandCapture(0, SHELL_COMMAND_ERASE_DALVIK_CACHE));
595 } catch (TimeoutException e) {
596 e.printStackTrace();
597 }
598 for (String tmpCmd : SHELL_COMMANDS_ERASE_DATA) {
Tiago Costad7e951f2015-03-02 17:37:33 +0000599 try {
600 Shell.runRootCommand(new CommandCapture(0, tmpCmd));
601 } catch (TimeoutException e) {
602 e.printStackTrace();
603 }
604 }
605 }else{
606 throw new RootDeniedException("Root Denied");
607 }
608 }
609 }
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000610
Jose Pascoal4fe7ba52015-03-13 16:54:31 +0000611 public static String getPath(final Context context, final Uri uri)
612 {
613 String filePath = uri.getPath();
614 if ("content".equalsIgnoreCase(uri.getScheme())) {
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000615
Jose Pascoal4fe7ba52015-03-13 16:54:31 +0000616 //Get the zip file name
617 String[] path = filePath.split("/");
618 String downloadIdStr = "";
619 if (path != null && path.length > 0) {
620 downloadIdStr = path[path.length - 1];
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000621 }
Jose Pascoal4fe7ba52015-03-13 16:54:31 +0000622 long downloadId = 0;
623 try {
624 downloadId = Long.parseLong(downloadIdStr);
625 } catch (NumberFormatException nfe) {
626 Log.w(TAG, "NumberFormatException: " + nfe.getMessage());
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000627 }
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000628
Jose Pascoal4fe7ba52015-03-13 16:54:31 +0000629 DownloadManager downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
630 DownloadManager.Query query = new DownloadManager.Query();
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000631
Jose Pascoal4fe7ba52015-03-13 16:54:31 +0000632 query.setFilterById(downloadId);
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000633
Jose Pascoal4fe7ba52015-03-13 16:54:31 +0000634 Cursor cursor = downloadManager != null ? downloadManager.query(query) : null;
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000635
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000636 if (cursor != null && cursor.moveToFirst()) {
Jose Pascoal4fe7ba52015-03-13 16:54:31 +0000637 int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
638 int status = cursor.getInt(columnIndex);
639
640 switch (status) {
641 case DownloadManager.STATUS_SUCCESSFUL: {
642 filePath = downloadManager.getUriForDownloadedFile(downloadId).getPath();
643 break;
644 }
645 case DownloadManager.STATUS_FAILED:
646 case DownloadManager.STATUS_PAUSED:
647 case DownloadManager.STATUS_PENDING:
648 case DownloadManager.STATUS_RUNNING:
649 default:
650 filePath = "";
651 break;
652 }
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000653 }
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000654 }
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000655
Jose Pascoal4fe7ba52015-03-13 16:54:31 +0000656 return filePath;
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000657 }
Filipe Gonçalves72817782015-03-19 15:29:05 +0000658
659 public static boolean isWiFiEnabled(Context context)
660 {
661
662 ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
663
664 return manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).isConnectedOrConnecting();
665 }
Jose Pascoal055562d2015-03-19 19:31:38 +0000666
Maarten Derkse2f0ffc2016-04-26 11:58:36 +0200667 public static boolean isBatteryLevelOk(Context context) {
668 IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
669 Intent batteryStatus = context.registerReceiver(null, ifilter);
670 int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
671 int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
672
673 float batteryPct = level / (float)scale;
674
675 if(batteryPct >= Float.parseFloat(context.getResources().getString(R.string.minimumBatteryLevel))) {
676 return true;
677 }
678 return false;
679 }
680
Jose Pascoal055562d2015-03-19 19:31:38 +0000681 public static Store getGappsStore()
682 {
683 return UpdaterData.getInstance().getStore(GAPPS_STORE_NUMBER);
684 }
Jose Pascoal02b849e2014-06-26 17:07:51 +0100685}