/** * */ package com.asksven.android.common.kernelutils; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import com.asksven.andoid.common.contrib.Shell; import com.asksven.andoid.common.contrib.Util; import com.asksven.android.common.CommonLogSettings; import com.asksven.android.common.privateapiproxies.NativeKernelWakelock; import com.asksven.android.common.privateapiproxies.NetworkUsage; import com.asksven.android.common.privateapiproxies.StatElement; import com.asksven.android.common.shellutils.Exec; import com.asksven.android.common.shellutils.ExecResult; import com.asksven.android.common.utils.StringUtils; import android.app.ActivityManager; import android.app.ActivityManager.RunningAppProcessInfo; import android.content.Context; import android.content.pm.PackageManager; import android.os.Build; import android.os.SystemClock; import android.util.Log; /** * @author sven * Parser for wakeup_sources * ********************************** * From pm_wakeup.h * struct wakeup_source - Representation of wakeup sources * * @total_time: Total time this wakeup source has been active. * @max_time: Maximum time this wakeup source has been continuously active. * @last_time: Monotonic clock when the wakeup source's was touched last time. * @prevent_sleep_time: Total time this source has been preventing autosleep. * @event_count: Number of signaled wakeup events. * @active_count: Number of times the wakeup sorce was activated. * @relax_count: Number of times the wakeup sorce was deactivated. * @expire_count: Number of times the wakeup source's timeout has expired. * @wakeup_count: Number of times the wakeup source might abort suspend. * @active: Status of the wakeup source. * @has_timeout: The wakeup source has been activated with a timeout. * struct wakeup_source { * const char *name; * struct list_head entry; * spinlock_t lock; * struct timer_list timer; * unsigned long timer_expires; * ktime_t total_time; * ktime_t max_time; * ktime_t last_time; * ktime_t start_prevent_time; * ktime_t prevent_sleep_time; * unsigned long event_count; * unsigned long active_count; * unsigned long relax_count; * unsigned long expire_count; * unsigned long wakeup_count; * bool active:1; * bool autosleep_enabled:1; * }; * */ public class WakeupSources extends Wakelocks { private final static String TAG ="WakeupSources"; private static String FILE_PATH = "/sys/kernel/debug/wakeup_sources"; //private static String FILE_PATH = "/sdcard/wakeup_sources.txt"; public static ArrayList<StatElement> parseWakeupSources(Context context) { Log.i(TAG, "Parsing " + FILE_PATH); String delimiter = String.valueOf('\t'); delimiter = delimiter + "+"; ArrayList<StatElement> myRet = new ArrayList<StatElement>(); // format // new [name active_count event_count wakeup_count expire_count active_since total_time max_time last_change prevent_suspend_time] ArrayList<String[]> rows = parseDelimitedFile(FILE_PATH, delimiter); long msSinceBoot = SystemClock.elapsedRealtime(); // list the running processes ActivityManager actvityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); List<RunningAppProcessInfo> procInfos = actvityManager.getRunningAppProcesses(); // start with 1 for (int i=1; i < rows.size(); i++ ) { try { // times in file are milliseconds String[] data = (String[]) rows.get(i); String name = data[0].trim(); // name int count = Integer.valueOf(data[1]); // active_count int expire_count = Integer.valueOf(data[4]); // expire_count int wake_count = Integer.valueOf(data[3]); // wakeup_count long active_since = Long.valueOf(data[5]); // active_since long total_time = Long.valueOf(data[6]); // total_time long sleep_time = Long.valueOf(data[9]); // prevent_suspend_time long max_time = Long.valueOf(data[7]); // max_time long last_change = Long.valueOf(data[8]); // last_change // post-processing of eventX-YYYY processes String details = ""; // we start with a " here as that is the way the data comes from /proc if (name.startsWith("\"event")) { String process = name.replaceAll("\"", ""); if (CommonLogSettings.DEBUG) { Log.d(TAG, "Pattern 'event' found in " + process); } int proc = 0; String[] parts = process.split("-"); if (parts.length == 2) { try { proc = Integer.valueOf(parts[1]); if (CommonLogSettings.DEBUG) { Log.d(TAG, "Resolving proc name for 'event' " + proc); } } catch (Exception e) { Log.e(TAG, "Cound not split process name " + process); } } if (proc != 0) { // search for the process in the task list for (int psCount = 0; psCount < procInfos.size(); psCount++) { int id = procInfos.get(psCount).pid; if ( id == proc) { String processName = procInfos.get(psCount).processName; details= processName; String appName = ""; String[] pkgList = procInfos.get(count).pkgList; for (int j=0; j < pkgList.length; j++) { if (details.length() > 0) { details += ", "; } details += pkgList[j]; } if (CommonLogSettings.DEBUG) { Log.d(TAG, "Pattern 'event' resolved to " + details); } } } } } if (CommonLogSettings.DEBUG) { Log.d(TAG, "Native Kernel wakelock parsed" + " name=" + name + " details=" + details + " count=" + count + " expire_count=" + expire_count + " wake_count=" + wake_count + " active_since=" + active_since + " total_time=" + total_time + " sleep_time=" + sleep_time + " max_time=" + max_time + "last_change=" + last_change + "ms_since_boot=" + msSinceBoot); } NativeKernelWakelock wl = null; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // on L sleep time is always 0 so use total time instead wl = new NativeKernelWakelock( name, details, count, expire_count, wake_count, active_since, total_time, total_time, max_time, last_change, msSinceBoot); } else { wl = new NativeKernelWakelock( name, details, count, expire_count, wake_count, active_since, total_time, sleep_time, max_time, last_change, msSinceBoot); } myRet.add(wl); } catch (Exception e) { // go on } } return myRet; } public static boolean fileExists() { boolean exists = false; FileReader fr = null; try { fr = new FileReader(FILE_PATH); exists = true; } catch (Exception e) { List<String> res = Shell.SU.run("cat " + FILE_PATH); if ((res == null)||(res.size()==0)) { exists = false; } else { exists = true; } } finally { if (fr != null && exists) { try { fr.close(); } catch (IOException e) { // do nothing } } } return exists; } }