blob: 2036f3d9d390cea68620c1dcf869f37dbf03ae4a [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';
Maarten Derksbb287762015-10-12 17:22:45 +020083 public static final String 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 }
375 filename.append(".zip");
Jose Pascoal40916302015-02-06 18:43:47 +0000376 return filename;
Jose Pascoal02d86242014-12-17 18:50:08 +0000377 }
378
Jose Pascoal40916302015-02-06 18:43:47 +0000379 public static String getDownloadTitleFromDownloadableItem(Resources resources, DownloadableItem item, boolean isVersion){
Jose Pascoal02d86242014-12-17 18:50:08 +0000380 String title = "";
381 if (item != null)
382 {
Jose Pascoal40916302015-02-06 18:43:47 +0000383 if (isVersion)
Jose Pascoal02d86242014-12-17 18:50:08 +0000384 {
385 Version version = (Version) item;
Maarten Derks1b51ea52016-03-08 09:02:14 +0100386 title = version.getHumanReadableName();
Jose Pascoal02d86242014-12-17 18:50:08 +0000387 }
Jose Pascoal40916302015-02-06 18:43:47 +0000388 else
Jose Pascoal02d86242014-12-17 18:50:08 +0000389 {
390 Store store = (Store) item;
391 title = store.getName();
392 }
393 }
394 return title;
395 }
Jose Pascoalda00b382015-01-20 18:39:28 +0000396
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000397 public static boolean isDeviceUnsupported(Context context)
Jose Pascoalda00b382015-01-20 18:39:28 +0000398 {
399 Version deviceVersion = VersionParserHelper.getDeviceVersion(context);
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000400 return deviceVersion == null || TextUtils.isEmpty(deviceVersion.getName());
Jose Pascoalda00b382015-01-20 18:39:28 +0000401 }
Filipe Gonçalves837e1572015-01-23 17:52:35 +0000402
Filipe72b5a7f2015-09-01 08:59:54 +0000403 private static Map<String,String> buildProps;
404 public static Map<String,String> getpropAll(){
405
406 if(buildProps==null) {
407 buildProps = new HashMap<>();
408 ProcessBuilder pb = new ProcessBuilder("/system/bin/getprop");
409 pb.redirectErrorStream(true);
410 Pattern propRegex = Pattern.compile("\\[([^\\]]+)\\]: \\[([^\\]]+)\\]");
411
412 Process p;
413 InputStream is = null;
414 try {
415 p = pb.start();
416 is = p.getInputStream();
417 Scanner scan = new Scanner(is);
418 String prop;
419 do {
420 prop = scan.nextLine();
421 Matcher match = propRegex.matcher(prop);
422 if(match.find()){
423 buildProps.put(match.group(1), match.group(2));
424 }
425 } while(!prop.isEmpty());
426 } catch (NoSuchElementException e) {
427 } catch (IOException e) {
Filipe Gonçalves837e1572015-01-23 17:52:35 +0000428 }
429 }
Filipe72b5a7f2015-09-01 08:59:54 +0000430 return buildProps;
Filipe Gonçalves837e1572015-01-23 17:52:35 +0000431 }
Filipe Gonçalves49ce23c2015-02-13 16:33:52 +0000432
Filipe72b5a7f2015-09-01 08:59:54 +0000433 public static String getprop(String name, String defaultValue)
434 {
435 String result;
436 if (getpropAll().containsKey(name)){
437 result = getpropAll().get(name);
438 } else {
439 result = defaultValue;
440 }
441 return result;
442 }
Filipe Gonçalves49ce23c2015-02-13 16:33:52 +0000443
Filipe72b5a7f2015-09-01 08:59:54 +0000444 public static void setBetaPropToEnable()
Filipe Gonçalves837e1572015-01-23 17:52:35 +0000445 {
446 if(RootTools.isAccessGiven()) {
Filipe72b5a7f2015-09-01 08:59:54 +0000447 CommandCapture command = new CommandCapture(0, "/system/bin/setprop "+ BetaEnabler.FAIRPHONE_BETA_PROPERTY+" "+BetaEnabler.BETA_ENABLED);
Filipe Gonçalves837e1572015-01-23 17:52:35 +0000448 try {
449 Shell.runRootCommand(command);
Filipe Gonçalvesb31d5862015-02-04 17:28:58 +0000450 } catch (IOException | TimeoutException | RootDeniedException e) {
451 Log.d(TAG, "Failed to setprop: " + e.getLocalizedMessage());
Filipe Gonçalves837e1572015-01-23 17:52:35 +0000452 }
453 }
Filipe72b5a7f2015-09-01 08:59:54 +0000454 try {
455 Thread.sleep(200);
456 } catch (InterruptedException e) {
457
458 }
459 buildProps = null;
Filipe Gonçalves837e1572015-01-23 17:52:35 +0000460 }
Jose Pascoal49f058a2015-02-13 16:58:01 +0000461
Jose Pascoal60c99372015-03-17 15:52:59 +0000462 public static String getOtaPackagePath(Resources resources, DownloadableItem item, boolean isVersion, boolean isZipInstall){
Jose Pascoal49f058a2015-02-13 16:58:01 +0000463 String path;
464
465 if (Utils.hasUnifiedPartition(resources))
466 {
467 path = resources.getString(R.string.recoveryCachePath) + Utils.getFilenameFromDownloadableItem(item, isVersion);
468 }
469 else
470 {
Jose Pascoal60c99372015-03-17 15:52:59 +0000471 if(isZipInstall && Build.MODEL.equalsIgnoreCase(resources.getString(R.string.FP1Model)))
472 {
473 //TODO: Find a way to not have this hardcoded
474 String zipPath = item.getDownloadLink();
475 path = zipPath.replace("/storage/sdcard0", resources.getString(R.string.recoverySdCardPath));
476 }
477 else
478 {
479 path = resources.getString(R.string.recoverySdCardPath) + resources.getString(R.string.updaterFolder) + Utils.getFilenameFromDownloadableItem(item, isVersion);
480 }
Jose Pascoal49f058a2015-02-13 16:58:01 +0000481 }
482
483 return path;
484 }
485
486 public static void writeCacheCommand(Context context, String otaPackagePath) throws IOException, TimeoutException, RootDeniedException, Resources.NotFoundException {
487 if (PrivilegeChecker.isPrivilegedApp()) {
488 File recovery_dir = new File("/cache/recovery/");
489 final boolean mkdirs = recovery_dir.mkdirs();
Filipe Gonçalves2ba0f552015-02-16 12:53:15 +0000490 if(! (mkdirs || recovery_dir.exists()) ) {
Jose Pascoal49f058a2015-02-13 16:58:01 +0000491 String errorMessage = context.getResources().getString(R.string.failed_mkdirs_cache_message);
492 Toast.makeText(context, errorMessage, Toast.LENGTH_LONG).show();
493 throw new IOException(errorMessage);
494 }
495
496 File command = new File("/cache/recovery/command");
497 File extendedCommand = new File("/cache/recovery/extendedcommand");
Filipe Gonçalves2ba0f552015-02-16 12:53:15 +0000498 final boolean deleteFailed = !extendedCommand.delete();
499 if (deleteFailed) {
Jose Pascoal49f058a2015-02-13 16:58:01 +0000500 Log.d(TAG, "Couldn't delete "+extendedCommand.getAbsolutePath());
501 }
502
503 String updateCommand = "--update_package=" + otaPackagePath;
504 PrintWriter writer = new PrintWriter(command, "UTF-8");
505 writer.println("--wipe_cache");
506 writer.println(updateCommand);
507 writer.flush();
508 writer.close();
509 }else {
510 if(RootTools.isAccessGiven()) {
511 Shell.runRootCommand(new CommandCapture(0, "rm -f /cache/recovery/command"));
512 Shell.runRootCommand(new CommandCapture(0, "rm -f /cache/recovery/extendedcommand"));
513 Shell.runRootCommand(new CommandCapture(0, "echo '--wipe_cache' >> /cache/recovery/command"));
514 Shell.runRootCommand(new CommandCapture(0, "echo '--update_package=" + otaPackagePath + "' >> /cache/recovery/command"));
515 }else{
516 throw new RootDeniedException("Root Denied");
517 }
518 }
519 }
520
Filipe Gonçalves2ba0f552015-02-16 12:53:15 +0000521 @SuppressWarnings("BooleanMethodIsAlwaysInverted")
Jose Pascoal49f058a2015-02-13 16:58:01 +0000522 public static boolean rebootToRecovery(Context context) {
523 boolean result;
524 if (PrivilegeChecker.isPrivilegedApp()) {
525 ((PowerManager) context.getSystemService(Context.POWER_SERVICE)).reboot("recovery");
526 result = false;
527 } else {
528 if(RootTools.isAccessGiven()) {
529 try {
530 Shell.runRootCommand(new CommandCapture(0, "reboot recovery"));
531 result = true;
532 } catch (IOException | TimeoutException | RootDeniedException e) {
533 Log.e(TAG, "Error rebooting to recovery: " + e.getLocalizedMessage());
534 result = false;
535 }
536 }else{
537 result = false;
538 }
539
540 }
541 return result;
542 }
Jose Pascoal87758742015-01-28 20:00:22 +0000543
Jose Pascoalcdd82042015-02-06 13:04:26 +0000544// --Commented out by Inspection START (06/02/2015 12:25):
545// public static void printStack(String moreLogs)
546// {
547// StringBuilder sb = new StringBuilder(moreLogs);
548// sb.append("\nStack --> ");
549// for (StackTraceElement ste : Thread.currentThread().getStackTrace())
550// {
551// sb.append(ste.getFileName()).append(" : ").append(ste.getMethodName()).append(":").append(ste.getLineNumber()).append(" -- ");
552// }
553// Log.wtf(TAG, sb.toString());
554// }
555// --Commented out by Inspection STOP (06/02/2015 12:25)
Jose Pascoal288dd2f2015-02-20 19:19:04 +0000556
557 public static boolean fileExists(String otaPackagePath) {
558 boolean fileExists;
559 if(PrivilegeChecker.isPrivilegedApp()){
560 File f = new File(otaPackagePath);
561 fileExists = f.exists();
562 }else {
563 fileExists = RootTools.exists(otaPackagePath);
564 }
565 return fileExists;
566 }
Tiago Costad7e951f2015-03-02 17:37:33 +0000567
Tiago Costa3d30c7b2015-03-03 16:11:52 +0000568 private final static String[] SHELL_COMMANDS_ERASE_DATA = {
Tiago Costad7e951f2015-03-02 17:37:33 +0000569 // remove data
570 "rm -rf /data/data/com.android.providers.media*",
571 "rm -rf /data/data/com.android.keychain*",
572 "rm -rf /data/data/com.android.location.fused*",
573 "rm -rf /data/data/com.android.providers.applications*",
574 "rm -rf /data/data/com.android.providers.media*",
575 "rm -rf /data/data/com.android.vending*",
576 "rm -rf /data/data/com.google.android.apps.genie.geniewidget*",
577 "rm -rf /data/data/com.google.android.apps.plus*",
578 "rm -rf /data/data/com.google.android.ears*",
579 "rm -rf /data/data/com.google.android.gms*",
580 "rm -rf /data/data/com.google.android.googlequicksearchbox*",
581 "rm -rf /data/data/com.google.android.location*",
582 "rm -rf /data/data/com.google.android.marvin.talkback*",
583 // remove cache
Tiago Costa3d30c7b2015-03-03 16:11:52 +0000584// "rm -rf /data/dalvik-cache",
Tiago Costad7e951f2015-03-02 17:37:33 +0000585 // remove data/app
586 "rm -rf /data/app/com.android.apps.plus*",
587 "rm -rf /data/app/com.android.vending*",
588 "rm -rf /data/app/com.android.easr*",
589 "rm -rf /data/app/com.android.gms*",
590 "rm -rf /data/app/com.android.tts*"
591 };
592
Tiago Costa3d30c7b2015-03-03 16:11:52 +0000593 public final static String SHELL_COMMAND_ERASE_DALVIK_CACHE = "rm -rf /data/dalvik-cache";
Tiago Costad7e951f2015-03-02 17:37:33 +0000594 public final static String SHELL_COMMAND_EXIT = "exit";
595
596 public static void clearGappsData() throws RootDeniedException, IOException, InterruptedException {
597
598 if (PrivilegeChecker.isPrivilegedApp()) {
Tiago Costa3d30c7b2015-03-03 16:11:52 +0000599 Process p = Runtime.getRuntime().exec(SHELL_COMMAND_ERASE_DALVIK_CACHE);
Tiago Costad7e951f2015-03-02 17:37:33 +0000600 DataOutputStream os = new DataOutputStream(p.getOutputStream());
Tiago Costa3d30c7b2015-03-03 16:11:52 +0000601 for (String tmpCmd : SHELL_COMMANDS_ERASE_DATA) {
Tiago Costad7e951f2015-03-02 17:37:33 +0000602 os.writeBytes(tmpCmd+"\n");
603 }
604 os.writeBytes(SHELL_COMMAND_EXIT+"\n");
605 os.flush();
606 p.waitFor();
607 }else {
608 if(RootTools.isAccessGiven()) {
Tiago Costa3d30c7b2015-03-03 16:11:52 +0000609 try {
610 Shell.runRootCommand(new CommandCapture(0, SHELL_COMMAND_ERASE_DALVIK_CACHE));
611 } catch (TimeoutException e) {
612 e.printStackTrace();
613 }
614 for (String tmpCmd : SHELL_COMMANDS_ERASE_DATA) {
Tiago Costad7e951f2015-03-02 17:37:33 +0000615 try {
616 Shell.runRootCommand(new CommandCapture(0, tmpCmd));
617 } catch (TimeoutException e) {
618 e.printStackTrace();
619 }
620 }
621 }else{
622 throw new RootDeniedException("Root Denied");
623 }
624 }
625 }
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000626
Jose Pascoal4fe7ba52015-03-13 16:54:31 +0000627 public static String getPath(final Context context, final Uri uri)
628 {
629 String filePath = uri.getPath();
630 if ("content".equalsIgnoreCase(uri.getScheme())) {
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000631
Jose Pascoal4fe7ba52015-03-13 16:54:31 +0000632 //Get the zip file name
633 String[] path = filePath.split("/");
634 String downloadIdStr = "";
635 if (path != null && path.length > 0) {
636 downloadIdStr = path[path.length - 1];
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000637 }
Jose Pascoal4fe7ba52015-03-13 16:54:31 +0000638 long downloadId = 0;
639 try {
640 downloadId = Long.parseLong(downloadIdStr);
641 } catch (NumberFormatException nfe) {
642 Log.w(TAG, "NumberFormatException: " + nfe.getMessage());
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000643 }
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000644
Jose Pascoal4fe7ba52015-03-13 16:54:31 +0000645 DownloadManager downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
646 DownloadManager.Query query = new DownloadManager.Query();
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000647
Jose Pascoal4fe7ba52015-03-13 16:54:31 +0000648 query.setFilterById(downloadId);
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000649
Jose Pascoal4fe7ba52015-03-13 16:54:31 +0000650 Cursor cursor = downloadManager != null ? downloadManager.query(query) : null;
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000651
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000652 if (cursor != null && cursor.moveToFirst()) {
Jose Pascoal4fe7ba52015-03-13 16:54:31 +0000653 int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
654 int status = cursor.getInt(columnIndex);
655
656 switch (status) {
657 case DownloadManager.STATUS_SUCCESSFUL: {
658 filePath = downloadManager.getUriForDownloadedFile(downloadId).getPath();
659 break;
660 }
661 case DownloadManager.STATUS_FAILED:
662 case DownloadManager.STATUS_PAUSED:
663 case DownloadManager.STATUS_PENDING:
664 case DownloadManager.STATUS_RUNNING:
665 default:
666 filePath = "";
667 break;
668 }
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000669 }
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000670 }
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000671
Jose Pascoal4fe7ba52015-03-13 16:54:31 +0000672 return filePath;
Jose Pascoal2a8283b2015-03-13 14:43:42 +0000673 }
Filipe Gonçalves72817782015-03-19 15:29:05 +0000674
675 public static boolean isWiFiEnabled(Context context)
676 {
677
678 ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
679
680 return manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).isConnectedOrConnecting();
681 }
Jose Pascoal055562d2015-03-19 19:31:38 +0000682
683 public static Store getGappsStore()
684 {
685 return UpdaterData.getInstance().getStore(GAPPS_STORE_NUMBER);
686 }
Jose Pascoal02b849e2014-06-26 17:07:51 +0100687}