/* * The MIT License (MIT) * Copyright (c) 2014 He Xiaocong (xiaocong@gmail.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE * OR OTHER DEALINGS IN THE SOFTWARE. */ package org.tecrash.crashreport.util; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.os.Build; import android.os.SystemClock; import android.preference.PreferenceManager; import android.telephony.TelephonyManager; import org.tecrash.crashreport.R; import org.tecrash.crashreport.ReportApp; import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Created by xiaocong on 21/11/14. */ public class Util { private final static String LOG_REG = "Log:\\s+([\\w\\-\\./:$#\\(\\)]+)"; private final static Pattern LOG_PATTERN = Pattern.compile(LOG_REG); private final static String PROCESS_REG = "Process:\\s+([\\w\\-\\./:$#\\(\\)]+)"; private final static Pattern PROCESS_PATTERN = Pattern.compile(PROCESS_REG); private final static String TOMBSTONE_REG = "pid:\\s*\\d+,\\s*tid:\\s*\\d+,\\s*name:.+?>>>\\s+([\\w\\-\\./:$#\\(\\)]+)\\s+<<<"; private final static Pattern TOMBSTONE_PATTERN = Pattern.compile(TOMBSTONE_REG); static Map<String, IProcess> tags = new HashMap<String, IProcess>(); static int MAX_LINES = 30; private static String wifiMacAddress = null; private static String serialNo = null; private static String UA = null; public static String parseTombstoneProcessName(String log) { int index = 0; for (String line : log.split(System.getProperty("line.separator"))) { Matcher m = TOMBSTONE_PATTERN.matcher(line); if (m.find()) { return m.group(1); } if (++index > MAX_LINES) break; } return null; } public static String parseProcessName(String log) { int index = 0; for (String line : log.split(System.getProperty("line.separator"))) { Matcher m = PROCESS_PATTERN.matcher(line); if (m.find()) { return m.group(1); } if (++index > MAX_LINES) break; } return null; } public static String parseLogPath(String log) { int index = 0; for (String line : log.split(System.getProperty("line.separator"))) { Matcher m = LOG_PATTERN.matcher(line); if (m.find()) { return m.group(1); } if (++index > MAX_LINES) break; } return null; } public static Map<String, IProcess> getTags() { if (tags.isEmpty()) { IProcess battery = new IProcess() { @Override public String getProcessName(String tag, String log) { return "battery"; } @Override public String getLogPath(String tag, String log) { return parseLogPath(log); } }; IProcess ss = new IProcess() { @Override public String getProcessName(String tag, String log) { return "system_server"; } @Override public String getLogPath(String tag, String log) { return parseLogPath(log); } }; IProcess kernel = new IProcess() { @Override public String getProcessName(String tag, String log) { return "kernel"; } @Override public String getLogPath(String tag, String log) { return null; } }; IProcess system = new IProcess() { @Override public String getProcessName(String tag, String log) { return "system"; } @Override public String getLogPath(String tag, String log) { return null; } }; IProcess save_as_tag = new IProcess() { @Override public String getProcessName(String tag, String log) { return tag; } @Override public String getLogPath(String tag, String log) { return parseLogPath(log); } }; IProcess app = new IProcess() { @Override public String getProcessName(String tag, String log) { return parseProcessName(log); } @Override public String getLogPath(String tag, String log) { return parseLogPath(log); } }; IProcess tombstone = new IProcess() { @Override public String getProcessName(String tag, String log) { return parseTombstoneProcessName(log); } @Override public String getLogPath(String tag, String log) { return parseLogPath(log); } }; tags.put("SYSTEM_RESTART", ss); tags.put("SYSTEM_TOMBSTONE", tombstone); tags.put("system_server_lowmem", ss); tags.put("system_server_watchdog", ss); tags.put("system_server_wtf", ss); tags.put("system_app_crash", app); tags.put("data_app_crash", app); tags.put("system_app_anr", app); tags.put("data_app_anr", app); tags.put("system_app_wtf", app); tags.put("BATTERY_DISCHARGE_INFO", battery); tags.put("SYSTEM_FSCK", kernel); tags.put("SYSTEM_AUDIT", kernel); tags.put("SYSTEM_LAST_KMSG", kernel); tags.put("APANIC_CONSOLE", kernel); tags.put("APANIC_THREADS", kernel); tags.put("SYSTEM_RECOVERY_LOG", system); tags.put("SYSTEM_BOOT", system); if (isDevelopment()) tags.put("system_app_strictmode", app); } return tags; } public static String getUploadURL() { return getURL().replaceFirst("https", "http"); } public static String getURL() { Context app = ReportApp.getInstance(); String urlKey = PreferenceManager.getDefaultSharedPreferences(app).getString(app.getString(R.string.pref_key_url), ""); if ("".equals(urlKey)) { String[] urls = ReportApp.getInstance().getResources().getStringArray(R.array.pref_key_url_list_values); urlKey = isDevelopment() ? urls[2] : urls[1]; } return getmetaDataString(urlKey); /* * "app.getApplicationInfo().metaData" throw nullpointer exception in prod version * and works well in dev version with unkown reason, * so change to "app.getPackageManager().getApplicationInfo(app.getPackageName(), PackageManager.GET_META_DATA)" */ //return app.getApplicationInfo().metaData.getString(urlKey); } public static long getDismissDays() { Context app = ReportApp.getInstance(); String days = PreferenceManager.getDefaultSharedPreferences(app).getString(app.getString(R.string.pref_key_days_to_upload_log), "90"); try { return Long.parseLong(days); } catch (NumberFormatException e) { return 30L; } } public static long getLastEntryTimestamp() { Context app = ReportApp.getInstance(); long ts = PreferenceManager.getDefaultSharedPreferences(app).getLong(preferenceKey(), System.currentTimeMillis() - SystemClock.elapsedRealtime()); if (ts > System.currentTimeMillis()) ts = System.currentTimeMillis(); return ts; } public static void setLastEntryTimestamp(long timestamp) { Context app = ReportApp.getInstance(); PreferenceManager.getDefaultSharedPreferences(app) .edit() .putLong(preferenceKey(), timestamp) .commit(); } private static String preferenceKey() { return "last_entry_timestamp"; } public static boolean isEnabled() { Context app = ReportApp.getInstance(); return PreferenceManager.getDefaultSharedPreferences(app).getBoolean(app.getString(R.string.pref_key_on), true); } public static long getUptimesInterval(){ return Build.TYPE.equals("user")? 1000 * 60 * 60 * 24 : 1000 * 60 * 60 * 24; } public static long getMaxDelayTimes() { return isDevelopment() ? 5 * 1000 : 10 * 60 * 1000; } public static boolean isDevelopment() { String packageName = ReportApp.getInstance().getPackageName(); return packageName.endsWith("development") || packageName.endsWith("develop") || packageName.endsWith("dev"); } public static String getWifiMacAddress() { if (wifiMacAddress == null) { wifiMacAddress = readText("/sys/class/net/wlan0/address"); } return wifiMacAddress; } public static String readText(String fileName) { StringBuilder sb = new StringBuilder(); try { BufferedReader br = new BufferedReader(new FileReader(fileName)); try { String line = br.readLine(); while (line != null) { sb.append(line); sb.append(System.getProperty("line.separator")); line = br.readLine(); } } finally { br.close(); } } catch (FileNotFoundException e) { } catch (IOException e) { } String txt = sb.toString(); return txt.isEmpty() ? null : txt.trim(); } public static String getSerialNo() { // ro.serialno is official property if (serialNo == null) { String[] keys = {"ro.serialno"}; for (String key : keys) { String sn = readProperty(key); if (sn != null && !sn.isEmpty()) serialNo = sn; } } return serialNo; } public static String readProperty(String key) { BufferedReader reader = null; String prop = null; try { Process proc = Runtime.getRuntime().exec(new String[]{"/system/bin/getprop", key}); reader = new BufferedReader(new InputStreamReader(proc.getInputStream())); prop = reader.readLine(); } catch (IOException e) { e.printStackTrace(); } finally { if (reader != null) { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } } return prop == null ? null : prop.trim(); } private static String _key = null; public static String getKey() { if (_key == null) { if (isDevelopment()) _key = "Bearer " + getmetaDataString("DROPBOX_DEVKEY"); else _key = "Bearer " + getmetaDataString("DROPBOX_APPKEY"); } return _key; } public static String getmetaDataString(String key){ String valueString = null; Context app = ReportApp.getInstance(); try { ApplicationInfo appInfo = app.getPackageManager().getApplicationInfo(ReportApp.getInstance().getPackageName(), PackageManager.GET_META_DATA); if(appInfo!=null){ valueString = appInfo.metaData.getString(key); } } catch (PackageManager.NameNotFoundException e){ e.printStackTrace(); } return valueString; } public static String getUserAgent() { if (UA == null) { String versionCode = "Unknown"; String appName = "Unknown"; PackageManager manager = ReportApp.getInstance().getPackageManager(); try { PackageInfo info = manager.getPackageInfo(ReportApp.getInstance().getPackageName(), 0); versionCode = String.valueOf(info.versionCode); appName = info.packageName; } catch (PackageManager.NameNotFoundException e) { } String language = Locale.getDefault().toString(); UA = String.format( "sdk_int=%1$s;app_version=%2$s;lang=%3$s;manufacturer=%4$s;model=%5$s;brand=%6$s;board=%7$s;device=%8$s;product=%9$s;incremental=%10$s;sn=%11$s;mac_address=%12$s;build_id=%13$s;app_name=%14$s;imei=%15$s", Build.VERSION.SDK_INT, versionCode, language, Build.MANUFACTURER, Build.MODEL, Build.BRAND, Build.BOARD, Build.DEVICE, Build.PRODUCT, Build.VERSION.INCREMENTAL, getSerialNo(), getWifiMacAddress(), Build.ID, appName, getIMEI() ); try { String extra = manager.getApplicationInfo(ReportApp.getInstance().getPackageName(), PackageManager.GET_META_DATA).metaData.getString("DROPBOX_REPORT_PROPERTIES", ""); if (!extra.isEmpty()) { for(String prop: extra.split(";")) { String[] pair = prop.split("="); if (pair.length == 2) { UA += ";" + pair[0] + "=" + readProperty(pair[1]); } } } } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } } return UA; } public static String getIMEI(){ Context app = ReportApp.getInstance(); TelephonyManager tm = (TelephonyManager) app.getSystemService(Context.TELEPHONY_SERVICE); if(tm.getDeviceId()==null||tm.getDeviceId().isEmpty()){ return "0"; } return tm.getDeviceId(); } public interface IProcess { public String getProcessName(String tag, String log); public String getLogPath(String tag, String log); } }