blob: 1fc64cc0cb9229797bbd9a5c818bbd738d44a5a2 [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;
Jose Pascoal0a5be012014-11-17 16:55:40 +000022import android.content.res.Resources;
Jose Pascoal2a8283b2015-03-13 14:43:42 +000023import android.database.Cursor;
Filipe Gonçalves72817782015-03-19 15:29:05 +000024import android.net.ConnectivityManager;
Jose Pascoal2a8283b2015-03-13 14:43:42 +000025import android.net.Uri;
Jose Pascoal54b3ae62014-10-07 20:29:58 +010026import android.os.Build;
Jose Pascoal0a5be012014-11-17 16:55:40 +000027import android.os.Environment;
Jose Pascoal49f058a2015-02-13 16:58:01 +000028import android.os.PowerManager;
Jose Pascoalda00b382015-01-20 18:39:28 +000029import android.text.TextUtils;
Jose Pascoal02b849e2014-06-26 17:07:51 +010030import android.util.Log;
Jose Pascoal49f058a2015-02-13 16:58:01 +000031import android.widget.Toast;
Jose Pascoal02b849e2014-06-26 17:07:51 +010032
Jose Pascoal46fdb062015-02-05 18:59:32 +000033import com.fairphone.updater.BetaEnabler;
Jose Pascoal0a5be012014-11-17 16:55:40 +000034import com.fairphone.updater.R;
Jose Pascoale4c48e32014-10-03 19:55:39 +010035import com.fairphone.updater.UpdaterService;
Jose Pascoal02d86242014-12-17 18:50:08 +000036import com.fairphone.updater.data.DownloadableItem;
37import com.fairphone.updater.data.Store;
Jose Pascoal055562d2015-03-19 19:31:38 +000038import com.fairphone.updater.data.UpdaterData;
Jose Pascoal7bf83a02014-10-13 18:30:18 +010039import com.fairphone.updater.data.Version;
40import com.fairphone.updater.data.VersionParserHelper;
Filipe Gonçalves837e1572015-01-23 17:52:35 +000041import com.stericson.RootTools.RootTools;
42import com.stericson.RootTools.exceptions.RootDeniedException;
43import com.stericson.RootTools.execution.CommandCapture;
44import com.stericson.RootTools.execution.Shell;
Jose Pascoal61e24762014-09-30 19:44:36 +010045
Tiago Costad7e951f2015-03-02 17:37:33 +000046import java.io.DataOutputStream;
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +000047import java.io.File;
48import java.io.FileInputStream;
49import java.io.FileNotFoundException;
Filipe Gonçalves49ce23c2015-02-13 16:33:52 +000050import java.io.FileOutputStream;
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +000051import java.io.IOException;
52import java.io.InputStream;
Jose Pascoal49f058a2015-02-13 16:58:01 +000053import java.io.PrintWriter;
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +000054import java.math.BigInteger;
Filipe Gonçalves49ce23c2015-02-13 16:33:52 +000055import java.nio.channels.FileChannel;
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +000056import java.security.MessageDigest;
57import java.security.NoSuchAlgorithmException;
Filipe72b5a7f2015-09-01 08:59:54 +000058import java.util.HashMap;
59import java.util.Map;
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +000060import java.util.NoSuchElementException;
61import java.util.Scanner;
62import java.util.concurrent.TimeoutException;
Filipe72b5a7f2015-09-01 08:59:54 +000063import java.util.regex.Matcher;
64import java.util.regex.Pattern;
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +000065
Jose Pascoal810950b2014-10-09 17:16:08 +010066public class Utils
67{
Jose Pascoal810950b2014-10-09 17:16:08 +010068 private static final String TAG = Utils.class.getSimpleName();
Jose Pascoal46fdb062015-02-05 18:59:32 +000069 private static final int DELAY_100_MILLIS = 100;
70 public static final int DELAY_HALF_SECOND = 500;
Jose Pascoalcfc2dd42015-02-09 18:00:05 +000071 public static final long SECONDS_IN_MINUTE = 60L;
72 public static final long MINUTES_IN_HOUR = 60L;
Jose Pascoal46fdb062015-02-05 18:59:32 +000073
74 private static final double BUFFER_1024_BYTES = 1024d;
Jose Pascoalcdd82042015-02-06 13:04:26 +000075 // --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 +000076 public static final int BUFFER_SIZE_2_KBYTES = 2048;
77 private static final int BUFFER_SIZE_8_KBYTES = 8192;
78 public static final int BUFFER_SIZE_10_MBYTES = 10240;
79 private static final int RADIX_BASE_16 = 16;
80 private static final double PERCENT_100 = 100d;
Jose Pascoalcfc2dd42015-02-09 18:00:05 +000081 private static final char CHAR_SPACE = ' ';
82 private static final char CHAR_ZERO = '0';
Jose Pascoal055562d2015-03-19 19:31:38 +000083 public static final int GAPPS_STORE_NUMBER = 0;
Jose Pascoal02b849e2014-06-26 17:07:51 +010084
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +000085 private static double getPartitionSizeInGBytes(File path)
Jose Pascoal810950b2014-10-09 17:16:08 +010086 {
87 double availableBlocks = getPartitionSizeInBytes(path);
Jose Pascoal46fdb062015-02-05 18:59:32 +000088 double sizeInGB = ((availableBlocks / BUFFER_1024_BYTES) / BUFFER_1024_BYTES) / BUFFER_1024_BYTES;
Jose Pascoal810950b2014-10-09 17:16:08 +010089 Log.d(TAG, path.getPath() + " size(GB): " + sizeInGB);
90 return sizeInGB;
91 }
Jose Pascoal02b849e2014-06-26 17:07:51 +010092
Jose Pascoalcdd82042015-02-06 13:04:26 +000093// --Commented out by Inspection START (06/02/2015 12:26):
94// public static double getPartitionSizeInMBytes(File path)
95// {
96// double availableBlocks = getPartitionSizeInBytes(path);
97// double sizeInMB = ((availableBlocks / BUFFER_1024_BYTES)) / BUFFER_1024_BYTES;
98// return sizeInMB;
99// }
100// --Commented out by Inspection STOP (06/02/2015 12:26)
Jose Pascoale4c48e32014-10-03 19:55:39 +0100101
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000102 private static long getPartitionSizeInBytes(File path)
Jose Pascoal810950b2014-10-09 17:16:08 +0100103 {
104 android.os.StatFs stat = new android.os.StatFs(path.getPath());
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000105 long blockSize, blockCount;
Jose Pascoal46fdb062015-02-05 18:59:32 +0000106 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000107 blockSize = stat.getBlockSizeLong();
108 blockCount = stat.getBlockCountLong();
109 } else {
110 // deprectation warnings disabled due to the need to support SDK 17 (FP1)
111 //noinspection deprecation
112 blockSize = stat.getBlockSize();
113 //noinspection deprecation
114 blockCount = stat.getBlockCount();
115 }
116 return blockCount * blockSize;
Jose Pascoal810950b2014-10-09 17:16:08 +0100117 }
Jose Pascoale4c48e32014-10-03 19:55:39 +0100118
Jose Pascoal810950b2014-10-09 17:16:08 +0100119 public static long getAvailablePartitionSizeInBytes(File path)
120 {
121 android.os.StatFs stat = new android.os.StatFs(path.getPath());
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000122
123 long blockSize, blockCount;
Jose Pascoal46fdb062015-02-05 18:59:32 +0000124 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000125 blockSize = stat.getBlockSizeLong();
126 blockCount = stat.getAvailableBlocksLong();
127 } else {
128 // deprectation warnings disabled due to the need to support SDK 17 (FP1)
129 //noinspection deprecation
130 blockSize = stat.getBlockSize();
131 //noinspection deprecation
132 blockCount = stat.getAvailableBlocks();
133 }
134 return blockCount * blockSize;
Jose Pascoal810950b2014-10-09 17:16:08 +0100135 }
Jose Pascoale4c48e32014-10-03 19:55:39 +0100136
Jose Pascoal810950b2014-10-09 17:16:08 +0100137 public static void startUpdaterService(Context context, boolean forceDownload)
138 {
Filipe72b5a7f2015-09-01 08:59:54 +0000139 Intent i = new Intent(context, UpdaterService.class);
140 i.putExtra(UpdaterService.EXTRA_FORCE_CONFIG_FILE_DOWNLOAD, forceDownload);
141 context.startService(i);
Jose Pascoal810950b2014-10-09 17:16:08 +0100142 }
Jose Pascoale4c48e32014-10-03 19:55:39 +0100143
Jose Pascoalcdd82042015-02-06 13:04:26 +0000144// --Commented out by Inspection START (06/02/2015 12:26):
145// public static void stopUpdaterService(Context context)
146// {
147// boolean isRunning = isServiceRunning(context);
148//
149// if (isRunning)
150// {
151// Log.i(TAG, "Stoping Updater Service...");
152// Intent i = new Intent(context, UpdaterService.class);
153// context.stopService(i);
154// try
155// {
156// Thread.sleep(DELAY_100_MILLIS * 2);
157// } catch (InterruptedException e)
158// {
159// Log.w(TAG, "Stop Updater service delay error: " + e.getLocalizedMessage());
160// }
161// }
162// }
163// --Commented out by Inspection STOP (06/02/2015 12:26)
Jose Pascoale4c48e32014-10-03 19:55:39 +0100164
Jose Pascoal810950b2014-10-09 17:16:08 +0100165 // **************************************************************************************************************
166 // HELPERS
167 // **************************************************************************************************************
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100168
Jose Pascoal810950b2014-10-09 17:16:08 +0100169 public static boolean checkMD5(String md5, File updateFile)
170 {
Jose Pascoal54b3ae62014-10-07 20:29:58 +0100171
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000172 if (updateFile == null || !updateFile.exists())
Jose Pascoal810950b2014-10-09 17:16:08 +0100173 {
174 return false;
175 }
176
Jose Pascoalcfc2dd42015-02-09 18:00:05 +0000177 if (md5 == null || md5.isEmpty())
Jose Pascoal810950b2014-10-09 17:16:08 +0100178 {
179 Log.e(TAG, "MD5 String NULL or UpdateFile NULL");
180 return false;
181 }
182
183 String calculatedDigest = calculateMD5(updateFile);
184 if (calculatedDigest == null)
185 {
186 Log.e(TAG, "calculatedDigest NULL");
187 return false;
188 }
189
190 return calculatedDigest.equalsIgnoreCase(md5);
191 }
192
Filipe72b5a7f2015-09-01 08:59:54 +0000193 public static String calculateMD5(File updateFile)
Jose Pascoal810950b2014-10-09 17:16:08 +0100194 {
195 MessageDigest digest;
196 try
197 {
198 digest = MessageDigest.getInstance("MD5");
199 } catch (NoSuchAlgorithmException e)
200 {
201 Log.e(TAG, "Exception while getting Digest", e);
202 return null;
203 }
204
205 InputStream is;
206 try
207 {
208 is = new FileInputStream(updateFile);
209 } catch (FileNotFoundException e)
210 {
211 Log.e(TAG, "Exception while getting FileInputStream", e);
212 return null;
213 }
214
Jose Pascoal46fdb062015-02-05 18:59:32 +0000215 byte[] buffer = new byte[BUFFER_SIZE_8_KBYTES];
Jose Pascoal810950b2014-10-09 17:16:08 +0100216 int read;
217 try
218 {
219 while ((read = is.read(buffer)) > 0)
220 {
221 digest.update(buffer, 0, read);
222 }
223 byte[] md5sum = digest.digest();
224 BigInteger bigInt = new BigInteger(1, md5sum);
Jose Pascoal46fdb062015-02-05 18:59:32 +0000225 String output = bigInt.toString(RADIX_BASE_16);
Jose Pascoal810950b2014-10-09 17:16:08 +0100226 // Fill to 32 chars
Jose Pascoalcfc2dd42015-02-09 18:00:05 +0000227 output = String.format("%32s", output).replace(CHAR_SPACE, CHAR_ZERO);
Jose Pascoal810950b2014-10-09 17:16:08 +0100228 return output;
229 } catch (IOException e)
230 {
Jose Pascoalcfc2dd42015-02-09 18:00:05 +0000231 Log.e(TAG, "Error digesting MD5: " + e.getLocalizedMessage());
Tiago Costa7c47e2d2014-11-18 16:50:35 +0000232 return null;
233// throw new RuntimeException("Unable to process file for MD5", e);
Jose Pascoal810950b2014-10-09 17:16:08 +0100234 } finally
235 {
236 try
237 {
238 is.close();
239 } catch (IOException e)
240 {
241 Log.e(TAG, "Exception on closing MD5 input stream", e);
242 }
243 }
244 }
245
246 public static String getModelAndOS(Context context)
247 {
248 StringBuilder sb = new StringBuilder();
249
250 // attach the model and the os
251 sb.append("?");
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000252 sb.append("model=").append(Build.MODEL.replaceAll("\\s", ""));
Jose Pascoal810950b2014-10-09 17:16:08 +0100253 Version currentVersion = VersionParserHelper.getDeviceVersion(context);
254
255 if (currentVersion != null)
256 {
257 sb.append("&");
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000258 sb.append("os=").append(currentVersion.getAndroidVersion());
Jose Pascoal810950b2014-10-09 17:16:08 +0100259 }
260
261 return sb.toString();
262 }
Jose Pascoal0a5be012014-11-17 16:55:40 +0000263
Filipe Gonçalves49ce23c2015-02-13 16:33:52 +0000264 public static void copy(File src, File dst) throws IOException {
265 if (PrivilegeChecker.isPrivilegedApp()) {
266 copyPrivileged(src, dst);
267 } else {
268 copyUnprivileged(src, dst);
269 }
270 }
271
Filipe Gonçalves2ba0f552015-02-16 12:53:15 +0000272 private static void copyUnprivileged(File src, File dst) throws IOException {
Filipe Gonçalves49ce23c2015-02-13 16:33:52 +0000273 if (RootTools.isAccessGiven()) {
274 RootTools.copyFile(src.getPath(), dst.getPath(), false, false);
275 } else {
276 throw new IOException("No root permissions granted.");
277 }
278 }
279
Filipe Gonçalves2ba0f552015-02-16 12:53:15 +0000280 private static void copyPrivileged(File src, File dst) throws IOException {
Filipe Gonçalves49ce23c2015-02-13 16:33:52 +0000281 FileInputStream inStream = new FileInputStream(src);
282 FileOutputStream outStream = new FileOutputStream(dst);
283 FileChannel inChannel = inStream.getChannel();
284 FileChannel outChannel = outStream.getChannel();
285 inChannel.transferTo(0, inChannel.size(), outChannel);
286 inStream.close();
287 outStream.close();
288 }
289
Jose Pascoal0a5be012014-11-17 16:55:40 +0000290 public static void clearCache()
291 {
Jose Pascoal288dd2f2015-02-20 19:19:04 +0000292 if(PrivilegeChecker.isPrivilegedApp()) {
293 File f = Environment.getDownloadCacheDirectory();
294 File[] files = f.listFiles();
295 if (files != null) {
296 Log.d(TAG, "Size: " + files.length);
297 for (File file : files) {
298 String filename = file.getName();
Jose Pascoal0a5be012014-11-17 16:55:40 +0000299
Jose Pascoal288dd2f2015-02-20 19:19:04 +0000300 if (filename.endsWith(".zip")) {
301 final boolean delete = file.delete();
302 if (delete) {
303 Log.d(TAG, "Deleted file " + filename);
304 } else {
305 Log.d(TAG, "Failed to delete file " + filename);
306 }
307 }
308 }
309 }
310 } else {
311 if(RootTools.isAccessGiven()) {
312 try {
313 Shell.runRootCommand(new CommandCapture(0, "rm -f *.zip"));
314 } catch (IOException | TimeoutException |RootDeniedException e) {
315 Log.w(TAG, "Failed to clear cache: " + e.getLocalizedMessage());
316 }
317 }
Jose Pascoal0a5be012014-11-17 16:55:40 +0000318 }
319 }
320
321 public static boolean hasUnifiedPartition(Resources resources)
322 {
323 File path = Environment.getDataDirectory();
324 double sizeInGB = Utils.getPartitionSizeInGBytes(path);
Jose Pascoal46fdb062015-02-05 18:59:32 +0000325 double roundedSize = Math.ceil(sizeInGB * PERCENT_100) / PERCENT_100;
Jose Pascoal0a5be012014-11-17 16:55:40 +0000326 Log.d(TAG, "/data size: " + roundedSize + "Gb");
327
Jose Pascoal8d4458f2015-03-17 20:03:59 +0000328 double fp1DataPartitionSize = (double) resources.getInteger(R.integer.FP1DataPartitionSizeMb) / BUFFER_1024_BYTES;
Jose Pascoal0a5be012014-11-17 16:55:40 +0000329 // Add a little buffer to the 1gb default just in case
330 return roundedSize > fp1DataPartitionSize;
331 }
332
333 public static String getPartitionDownloadPath(Resources resources)
334 {
335 String downloadPath = "";
336 if (Build.MODEL.equals(resources.getString(R.string.FP1Model)))
337 {
338 downloadPath =
339 Utils.hasUnifiedPartition(resources) ? resources.getString(R.string.unifiedDataPartition) : resources
340 .getString(R.string.oneGBDataPartition);
341 }
342 return downloadPath;
343 }
344
345 public static boolean canCopyToCache(File file)
346 {
347 double fileSize = file.length();
348 double cacheSize = Utils.getPartitionSizeInBytes(Environment.getDownloadCacheDirectory());
Jose Pascoalb980c862015-03-12 20:22:02 +0000349 return fileSize > 0 && cacheSize >= fileSize;
Jose Pascoal0a5be012014-11-17 16:55:40 +0000350 }
Jose Pascoal40916302015-02-06 18:43:47 +0000351
352 public static String getFilenameFromDownloadableItem(DownloadableItem item, boolean isVersion)
Jose Pascoal02d86242014-12-17 18:50:08 +0000353 {
Jose Pascoalcfc2dd42015-02-09 18:00:05 +0000354 StringBuilder filename;
Jose Pascoal40916302015-02-06 18:43:47 +0000355
356 if(isVersion)
357 {
358 filename = getFilenameForItem(item, "update_");
359 }
360 else
361 {
362 filename = getFilenameForItem(item, "store_");
363 }
364
365 return filename.toString();
366 }
367
368 private static StringBuilder getFilenameForItem(DownloadableItem item, String type) {
Jose Pascoal02d86242014-12-17 18:50:08 +0000369 StringBuilder filename = new StringBuilder();
370 filename.append("fp_");
371 if (item != null)
372 {
Jose Pascoal40916302015-02-06 18:43:47 +0000373 filename.append(type);
Jose Pascoal02d86242014-12-17 18:50:08 +0000374 filename.append(item.getNumber());
375 }
376 filename.append(".zip");
Jose Pascoal40916302015-02-06 18:43:47 +0000377 return filename;
Jose Pascoal02d86242014-12-17 18:50:08 +0000378 }
379
Jose Pascoal40916302015-02-06 18:43:47 +0000380 public static String getDownloadTitleFromDownloadableItem(Resources resources, DownloadableItem item, boolean isVersion){
Jose Pascoal02d86242014-12-17 18:50:08 +0000381 String title = "";
382 if (item != null)
383 {
Jose Pascoal40916302015-02-06 18:43:47 +0000384 if (isVersion)
Jose Pascoal02d86242014-12-17 18:50:08 +0000385 {
386 Version version = (Version) item;
387 title = version.getName() + " " + version.getImageTypeDescription(resources);
388 }
Jose Pascoal40916302015-02-06 18:43:47 +0000389 else
Jose Pascoal02d86242014-12-17 18:50:08 +0000390 {
391 Store store = (Store) item;
392 title = store.getName();
393 }
394 }
395 return title;
396 }
Jose Pascoalda00b382015-01-20 18:39:28 +0000397
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000398 public static boolean isDeviceUnsupported(Context context)
Jose Pascoalda00b382015-01-20 18:39:28 +0000399 {
400 Version deviceVersion = VersionParserHelper.getDeviceVersion(context);
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000401 return deviceVersion == null || TextUtils.isEmpty(deviceVersion.getName());
Jose Pascoalda00b382015-01-20 18:39:28 +0000402 }
Filipe Gonçalves837e1572015-01-23 17:52:35 +0000403
Filipe72b5a7f2015-09-01 08:59:54 +0000404 private static Map<String,String> buildProps;
405 public static Map<String,String> getpropAll(){
406
407 if(buildProps==null) {
408 buildProps = new HashMap<>();
409 ProcessBuilder pb = new ProcessBuilder("/system/bin/getprop");
410 pb.redirectErrorStream(true);
411 Pattern propRegex = Pattern.compile("\\[([^\\]]+)\\]: \\[([^\\]]+)\\]");
412
413 Process p;
414 InputStream is = null;
415 try {
416 p = pb.start();
417 is = p.getInputStream();
418 Scanner scan = new Scanner(is);
419 String prop;
420 do {
421 prop = scan.nextLine();
422 Matcher match = propRegex.matcher(prop);
423 if(match.find()){
424 buildProps.put(match.group(1), match.group(2));
425 }
426 } while(!prop.isEmpty());
427 } catch (NoSuchElementException e) {
428 } catch (IOException e) {
Filipe Gonçalves837e1572015-01-23 17:52:35 +0000429 }
430 }
Filipe72b5a7f2015-09-01 08:59:54 +0000431 return buildProps;
Filipe Gonçalves837e1572015-01-23 17:52:35 +0000432 }
Filipe Gonçalves49ce23c2015-02-13 16:33:52 +0000433
Filipe72b5a7f2015-09-01 08:59:54 +0000434 public static String getprop(String name, String defaultValue)
435 {
436 String result;
437 if (getpropAll().containsKey(name)){
438 result = getpropAll().get(name);
439 } else {
440 result = defaultValue;
441 }
442 return result;
443 }
Filipe Gonçalves49ce23c2015-02-13 16:33:52 +0000444
Filipe72b5a7f2015-09-01 08:59:54 +0000445 public static void setBetaPropToEnable()
Filipe Gonçalves837e1572015-01-23 17:52:35 +0000446 {
447 if(RootTools.isAccessGiven()) {
Filipe72b5a7f2015-09-01 08:59:54 +0000448 CommandCapture command = new CommandCapture(0, "/system/bin/setprop "+ BetaEnabler.FAIRPHONE_BETA_PROPERTY+" "+BetaEnabler.BETA_ENABLED);
Filipe Gonçalves837e1572015-01-23 17:52:35 +0000449 try {
450 Shell.runRootCommand(command);
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000451 } catch (IOException | TimeoutException | RootDeniedException e) {
452 Log.d(TAG, "Failed to setprop: " + e.getLocalizedMessage());
Filipe Gonçalves837e1572015-01-23 17:52:35 +0000453 }
454 }
Filipe72b5a7f2015-09-01 08:59:54 +0000455 try {
456 Thread.sleep(200);
457 } catch (InterruptedException e) {
458
459 }
460 buildProps = null;
Filipe Gonçalves837e1572015-01-23 17:52:35 +0000461 }
Jose Pascoal49f058a2015-02-13 16:58:01 +0000462
Jose Pascoal60c99372015-03-17 15:52:59 +0000463 public static String getOtaPackagePath(Resources resources, DownloadableItem item, boolean isVersion, boolean isZipInstall){
Jose Pascoal49f058a2015-02-13 16:58:01 +0000464 String path;
465
466 if (Utils.hasUnifiedPartition(resources))
467 {
468 path = resources.getString(R.string.recoveryCachePath) + Utils.getFilenameFromDownloadableItem(item, isVersion);
469 }
470 else
471 {
Jose Pascoal60c99372015-03-17 15:52:59 +0000472 if(isZipInstall && Build.MODEL.equalsIgnoreCase(resources.getString(R.string.FP1Model)))
473 {
474 //TODO: Find a way to not have this hardcoded
475 String zipPath = item.getDownloadLink();
476 path = zipPath.replace("/storage/sdcard0", resources.getString(R.string.recoverySdCardPath));
477 }
478 else
479 {
480 path = resources.getString(R.string.recoverySdCardPath) + resources.getString(R.string.updaterFolder) + Utils.getFilenameFromDownloadableItem(item, isVersion);
481 }
Jose Pascoal49f058a2015-02-13 16:58:01 +0000482 }
483
484 return path;
485 }
486
487 public static void writeCacheCommand(Context context, String otaPackagePath) throws IOException, TimeoutException, RootDeniedException, Resources.NotFoundException {
488 if (PrivilegeChecker.isPrivilegedApp()) {
489 File recovery_dir = new File("/cache/recovery/");
490 final boolean mkdirs = recovery_dir.mkdirs();
Filipe Gonçalves2ba0f552015-02-16 12:53:15 +0000491 if(! (mkdirs || recovery_dir.exists()) ) {
Jose Pascoal49f058a2015-02-13 16:58:01 +0000492 String errorMessage = context.getResources().getString(R.string.failed_mkdirs_cache_message);
493 Toast.makeText(context, errorMessage, Toast.LENGTH_LONG).show();
494 throw new IOException(errorMessage);
495 }
496
497 File command = new File("/cache/recovery/command");
498 File extendedCommand = new File("/cache/recovery/extendedcommand");
Filipe Gonçalves2ba0f552015-02-16 12:53:15 +0000499 final boolean deleteFailed = !extendedCommand.delete();
500 if (deleteFailed) {
Jose Pascoal49f058a2015-02-13 16:58:01 +0000501 Log.d(TAG, "Couldn't delete "+extendedCommand.getAbsolutePath());
502 }
503
504 String updateCommand = "--update_package=" + otaPackagePath;
505 PrintWriter writer = new PrintWriter(command, "UTF-8");
506 writer.println("--wipe_cache");
507 writer.println(updateCommand);
508 writer.flush();
509 writer.close();
510 }else {
511 if(RootTools.isAccessGiven()) {
512 Shell.runRootCommand(new CommandCapture(0, "rm -f /cache/recovery/command"));
513 Shell.runRootCommand(new CommandCapture(0, "rm -f /cache/recovery/extendedcommand"));
514 Shell.runRootCommand(new CommandCapture(0, "echo '--wipe_cache' >> /cache/recovery/command"));
515 Shell.runRootCommand(new CommandCapture(0, "echo '--update_package=" + otaPackagePath + "' >> /cache/recovery/command"));
516 }else{
517 throw new RootDeniedException("Root Denied");
518 }
519 }
520 }
521
Filipe Gonçalves2ba0f552015-02-16 12:53:15 +0000522 @SuppressWarnings("BooleanMethodIsAlwaysInverted")
Jose Pascoal49f058a2015-02-13 16:58:01 +0000523 public static boolean rebootToRecovery(Context context) {
524 boolean result;
525 if (PrivilegeChecker.isPrivilegedApp()) {
526 ((PowerManager) context.getSystemService(Context.POWER_SERVICE)).reboot("recovery");
527 result = false;
528 } else {
529 if(RootTools.isAccessGiven()) {
530 try {
531 Shell.runRootCommand(new CommandCapture(0, "reboot recovery"));
532 result = true;
533 } catch (IOException | TimeoutException | RootDeniedException e) {
534 Log.e(TAG, "Error rebooting to recovery: " + e.getLocalizedMessage());
535 result = false;
536 }
537 }else{
538 result = false;
539 }
540
541 }
542 return result;
543 }
Jose Pascoal87758742015-01-28 20:00:22 +0000544
Jose Pascoalcdd82042015-02-06 13:04:26 +0000545// --Commented out by Inspection START (06/02/2015 12:25):
546// public static void printStack(String moreLogs)
547// {
548// StringBuilder sb = new StringBuilder(moreLogs);
549// sb.append("\nStack --> ");
550// for (StackTraceElement ste : Thread.currentThread().getStackTrace())
551// {
552// sb.append(ste.getFileName()).append(" : ").append(ste.getMethodName()).append(":").append(ste.getLineNumber()).append(" -- ");
553// }
554// Log.wtf(TAG, sb.toString());
555// }
556// --Commented out by Inspection STOP (06/02/2015 12:25)
Jose Pascoal288dd2f2015-02-20 19:19:04 +0000557
558 public static boolean fileExists(String otaPackagePath) {
559 boolean fileExists;
560 if(PrivilegeChecker.isPrivilegedApp()){
561 File f = new File(otaPackagePath);
562 fileExists = f.exists();
563 }else {
564 fileExists = RootTools.exists(otaPackagePath);
565 }
566 return fileExists;
567 }
Tiago Costad7e951f2015-03-02 17:37:33 +0000568
Tiago Costa3d30c7b2015-03-03 16:11:52 +0000569 private final static String[] SHELL_COMMANDS_ERASE_DATA = {
Tiago Costad7e951f2015-03-02 17:37:33 +0000570 // remove data
571 "rm -rf /data/data/com.android.providers.media*",
572 "rm -rf /data/data/com.android.keychain*",
573 "rm -rf /data/data/com.android.location.fused*",
574 "rm -rf /data/data/com.android.providers.applications*",
575 "rm -rf /data/data/com.android.providers.media*",
576 "rm -rf /data/data/com.android.vending*",
577 "rm -rf /data/data/com.google.android.apps.genie.geniewidget*",
578 "rm -rf /data/data/com.google.android.apps.plus*",
579 "rm -rf /data/data/com.google.android.ears*",
580 "rm -rf /data/data/com.google.android.gms*",
581 "rm -rf /data/data/com.google.android.googlequicksearchbox*",
582 "rm -rf /data/data/com.google.android.location*",
583 "rm -rf /data/data/com.google.android.marvin.talkback*",
584 // remove cache
Tiago Costa3d30c7b2015-03-03 16:11:52 +0000585// "rm -rf /data/dalvik-cache",
Tiago Costad7e951f2015-03-02 17:37:33 +0000586 // remove data/app
587 "rm -rf /data/app/com.android.apps.plus*",
588 "rm -rf /data/app/com.android.vending*",
589 "rm -rf /data/app/com.android.easr*",
590 "rm -rf /data/app/com.android.gms*",
591 "rm -rf /data/app/com.android.tts*"
592 };
593
Tiago Costa3d30c7b2015-03-03 16:11:52 +0000594 public final static String SHELL_COMMAND_ERASE_DALVIK_CACHE = "rm -rf /data/dalvik-cache";
Tiago Costad7e951f2015-03-02 17:37:33 +0000595 public final static String SHELL_COMMAND_EXIT = "exit";
596
597 public static void clearGappsData() throws RootDeniedException, IOException, InterruptedException {
598
599 if (PrivilegeChecker.isPrivilegedApp()) {
Tiago Costa3d30c7b2015-03-03 16:11:52 +0000600 Process p = Runtime.getRuntime().exec(SHELL_COMMAND_ERASE_DALVIK_CACHE);
Tiago Costad7e951f2015-03-02 17:37:33 +0000601 DataOutputStream os = new DataOutputStream(p.getOutputStream());
Tiago Costa3d30c7b2015-03-03 16:11:52 +0000602 for (String tmpCmd : SHELL_COMMANDS_ERASE_DATA) {
Tiago Costad7e951f2015-03-02 17:37:33 +0000603 os.writeBytes(tmpCmd+"\n");
604 }
605 os.writeBytes(SHELL_COMMAND_EXIT+"\n");
606 os.flush();
607 p.waitFor();
608 }else {
609 if(RootTools.isAccessGiven()) {
Tiago Costa3d30c7b2015-03-03 16:11:52 +0000610 try {
611 Shell.runRootCommand(new CommandCapture(0, SHELL_COMMAND_ERASE_DALVIK_CACHE));
612 } catch (TimeoutException e) {
613 e.printStackTrace();
614 }
615 for (String tmpCmd : SHELL_COMMANDS_ERASE_DATA) {
Tiago Costad7e951f2015-03-02 17:37:33 +0000616 try {
617 Shell.runRootCommand(new CommandCapture(0, tmpCmd));
618 } catch (TimeoutException e) {
619 e.printStackTrace();
620 }
621 }
622 }else{
623 throw new RootDeniedException("Root Denied");
624 }
625 }
626 }
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000627
Jose Pascoal4fe7ba52015-03-13 16:54:31 +0000628 public static String getPath(final Context context, final Uri uri)
629 {
630 String filePath = uri.getPath();
631 if ("content".equalsIgnoreCase(uri.getScheme())) {
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000632
Jose Pascoal4fe7ba52015-03-13 16:54:31 +0000633 //Get the zip file name
634 String[] path = filePath.split("/");
635 String downloadIdStr = "";
636 if (path != null && path.length > 0) {
637 downloadIdStr = path[path.length - 1];
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000638 }
Jose Pascoal4fe7ba52015-03-13 16:54:31 +0000639 long downloadId = 0;
640 try {
641 downloadId = Long.parseLong(downloadIdStr);
642 } catch (NumberFormatException nfe) {
643 Log.w(TAG, "NumberFormatException: " + nfe.getMessage());
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000644 }
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000645
Jose Pascoal4fe7ba52015-03-13 16:54:31 +0000646 DownloadManager downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
647 DownloadManager.Query query = new DownloadManager.Query();
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000648
Jose Pascoal4fe7ba52015-03-13 16:54:31 +0000649 query.setFilterById(downloadId);
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000650
Jose Pascoal4fe7ba52015-03-13 16:54:31 +0000651 Cursor cursor = downloadManager != null ? downloadManager.query(query) : null;
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000652
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000653 if (cursor != null && cursor.moveToFirst()) {
Jose Pascoal4fe7ba52015-03-13 16:54:31 +0000654 int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
655 int status = cursor.getInt(columnIndex);
656
657 switch (status) {
658 case DownloadManager.STATUS_SUCCESSFUL: {
659 filePath = downloadManager.getUriForDownloadedFile(downloadId).getPath();
660 break;
661 }
662 case DownloadManager.STATUS_FAILED:
663 case DownloadManager.STATUS_PAUSED:
664 case DownloadManager.STATUS_PENDING:
665 case DownloadManager.STATUS_RUNNING:
666 default:
667 filePath = "";
668 break;
669 }
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000670 }
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000671 }
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000672
Jose Pascoal4fe7ba52015-03-13 16:54:31 +0000673 return filePath;
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000674 }
Filipe Gonçalves72817782015-03-19 15:29:05 +0000675
676 public static boolean isWiFiEnabled(Context context)
677 {
678
679 ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
680
681 return manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).isConnectedOrConnecting();
682 }
Jose Pascoal055562d2015-03-19 19:31:38 +0000683
684 public static Store getGappsStore()
685 {
686 return UpdaterData.getInstance().getStore(GAPPS_STORE_NUMBER);
687 }
Jose Pascoal02b849e2014-06-26 17:07:51 +0100688}