/* * Software Name : ATK * * Copyright (C) 2007 - 2012 France T�l�com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ------------------------------------------------------------------ * File Name : ATKMonitorService.java * * Created : 17/02/2010 * Author(s) : Laurent Gottely */ package com.orange.atk.monitor.service; import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Vector; import android.app.ActivityManager; import android.app.ActivityManager.RunningAppProcessInfo; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.os.Build; import android.os.Debug; import android.os.Debug.MemoryInfo; import android.os.Environment; import android.os.IBinder; import android.os.RemoteException; import android.os.StatFs; import android.provider.Settings; import android.util.Log; import android.view.Display; import android.view.WindowManager; import com.orange.atk.monitor.IATKMonitorCom; import com.orange.atk.monitor.IATKMonitorEventListener; import com.orange.atk.monitor.R; public class ATKMonitorService extends ATKService implements Runnable{ private static final String TAG = "ATKMonitorService"; private final static int PORT = 1357; private static final int ATK_MONITOR = 0; private static int count = 0; private CPUThread cput; protected List <IATKMonitorEventListener> listeners = new ArrayList<IATKMonitorEventListener>(); private String totalmem = "0"; private int batteryLevel = 0; private StatFs internalStorage; private StatFs externalStorage; private long totalRxBytesOffset = -1; private long totalTxBytesOffset = -1; private HashMap<Integer, Long> uidRxBytesOffsets = new HashMap<Integer, Long>(); private HashMap<Integer, Long> uidTxBytesOffsets = new HashMap<Integer, Long>(); private final IATKMonitorCom.Stub mBinder = new IATKMonitorCom.Stub () { public String getCPU() throws RemoteException { return getCpu(); } public String getConnection() throws RemoteException { return getConnection(); } public String getMem() throws RemoteException { return getMem(); } public void addEventListener(IATKMonitorEventListener listener) throws RemoteException { listeners.add(listener); } public void removeAllEventListeners() throws RemoteException { listeners.clear(); } public void removeEventListener(IATKMonitorEventListener listener) throws RemoteException { listeners.remove(listener); } public void stop() throws RemoteException { quit(); return; } }; public void addNotification(boolean state, String label) { // Create notification PendingIntent contentIntent = PendingIntent.getActivity(_context, 0, new Intent("com.orange.atk.monitor.CLIENT"), Intent.FLAG_ACTIVITY_NEW_TASK); int iconId; if (state) { iconId = R.drawable.icon_monitor_on; } else { iconId = R.drawable.icon_monitor_off; } Notification notif = new Notification(iconId, "", System.currentTimeMillis()); notif.flags = Notification.FLAG_NO_CLEAR; notif.setLatestEventInfo( _context , "ATKMonitorService",label, contentIntent); // Send notification NotificationManager notificationManager = (NotificationManager)_context.getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.notify(ATK_MONITOR, notif); } @Override public IBinder onBind(Intent intent) { Log.v(TAG,"onBind"); onStart(intent,0); return mBinder; } public void onStart(Intent intent, int startId) { super.onStart(intent, startId); PendingIntent contentIntent = PendingIntent.getActivity(_context, 0, new Intent("com.orange.atk.monitor.CLIENT"), Intent.FLAG_ACTIVITY_NEW_TASK); int iconId = R.drawable.icon_monitor_on; Notification notif = new Notification(iconId, "", System.currentTimeMillis()); notif.flags = Notification.FLAG_NO_CLEAR; notif.setLatestEventInfo( _context , "ATKMonitorService","Monitor started", contentIntent); // Send notification NotificationManager notificationManager = (NotificationManager)_context.getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.notify(ATK_MONITOR, notif); // Internal Storage init String dataDir = Environment.getDataDirectory().getPath(); internalStorage = new StatFs(dataDir); Log.v(TAG,"Internal data directory = "+dataDir); // External Storage init dataDir = Environment.getExternalStorageDirectory().getPath(); externalStorage = new StatFs(dataDir); Log.v(TAG,"External data directory = "+dataDir); this.batteryLevel(); //temp_testings(); if (t==null) { Thread t2 = new Thread(this); t2.setName("ATKMonitorServiceThread"); t2.start(); } } private void notifyGlobalChange(String s, String totalmem){ IATKMonitorEventListener listener; int i; for (i=0; i < listeners.size();i++) { listener = (IATKMonitorEventListener)listeners.get(i); try { listener.globalChanged(s,totalmem); } catch (RemoteException e) { e.printStackTrace(); } } } protected void quit() { super.quit(); if(cput != null) cput.quit(); addNotification(false,"Monitor Stopped"); } protected String analyseInput(String inputline) { if( (++count%60)==0){ System.gc(); addNotification(true,"Monitor running"); Log.d(TAG, "update notification #"+count); } if (inputline.equals("VERSION")) { Log.v(TAG,"VERSION"); //Get the version PackageInfo pi = null; try { pi = this.getPackageManager().getPackageInfo(this.getPackageName(), 0); } catch (NameNotFoundException e) { Log.v(TAG, "Error : No version name in manifest"); } if (pi !=null) return pi.versionName; else return "0"; } else if (inputline.matches("INIT.+")) { Log.v(TAG,"INIT"); // reportCpu(); String [] listnameprocess = inputline.split(" +"); ProcessInformation processinformation; //by default, remove every process from the list cput.ClearProcessList(); //Parse the init command and add new process information in the list for(int i=1; i<listnameprocess.length; i++){ processinformation = new ProcessInformation(listnameprocess[i]); cput.Addprocesstolist(processinformation); int pid = getPidFromName(listnameprocess[i]); if(-1 != pid) processinformation.setPID(pid); } if (Integer.valueOf(Build.VERSION.SDK) >= 8) { totalRxBytesOffset = getTotalRxBytes(); totalTxBytesOffset = getTotalTxBytes(); // Log.v("TEST", "RxOffset="+totalRxBytesOffset+", TxOffset="+totalTxBytesOffset); }else { Log.v(TAG,"API level < 8 : No network data "); } return "Init Ok "; } else if (inputline.matches("RES")) { // Return resource information formatted as follow: // GLOBAL CPU_TOTAL PROCESSOR_SPEED MEM_USED STORAGE_DATA_USED BATTERY_LEVEL // Optional: // NAME_PROCESS CPU_USED MEM_VIRTUAL // ... //Log.v(TAG,"getCPU"); // reportCpu(); LinkedList<ProcessInformation> listprocess = cput.getListprocess(); ProcessInformation processinformation; String ressource = "GLOBAL "; String cpuLoad = String.valueOf((int) (cput.getCPU() * cput.getCPUfrequency() / 100.0)); ressource+= cpuLoad + " "; ressource+= getTotalMemoryUsed(); //Get the state of the internal and external storage for data internalStorage.restat(Environment.getDataDirectory().getPath()); externalStorage.restat(Environment.getExternalStorageDirectory().getPath()); // Sdcard long freeInData = (long)(internalStorage.getBlockCount()-internalStorage.getAvailableBlocks())*internalStorage.getBlockSize(); long freeExData = (long)(externalStorage.getBlockCount()-externalStorage.getAvailableBlocks())*externalStorage.getBlockSize(); //Log.v(TAG,"int blockCount "+internalStorage.getBlockCount()+" availableBlack "+internalStorage.getAvailableBlocks()+" blackSize "+internalStorage.getBlockSize()); ressource+=" "+String.valueOf(freeInData/1024L); if (externalStorage.getBlockCount()==0) ressource+=" -1"; else ressource+=" "+String.valueOf(freeExData/1024L); ressource+=" "+batteryLevel; // % long rX = -1000,tX = -1000 ; if (Integer.valueOf(Build.VERSION.SDK) >= 8) { long newTotalRx = getTotalRxBytes(); long newTotalTx = getTotalTxBytes(); rX = newTotalRx - totalRxBytesOffset; if (rX < 0) rX = 0; tX = newTotalTx - totalTxBytesOffset; if (tX < 0) tX = 0; totalRxBytesOffset = newTotalRx; totalTxBytesOffset = newTotalTx; } ressource += " " + rX; // % ressource += " " + tX; // % //Get info for the processes if(null != listprocess){ for(int i = 0;i < listprocess.size();i++){ int pid = -1; processinformation = listprocess.get(i); if (processinformation.isRunning(this._context)) { String nameprocess = processinformation.getProcess_name(); pid = processinformation.getPID(); if(-1 == pid){ pid = getPidFromName(nameprocess); // Test if the process is running if(-1 == pid){ // The process is not running, we set default value processinformation.setCpu_load(0); processinformation.setLast_used_cpu_process(0); } else{ processinformation.setPID(pid); } } int loadprocess = processinformation.getCpu_load(); ressource+="\n"+nameprocess+" "+String.valueOf(loadprocess); // Memory if(-1 != pid){ try { String m= getUsedMemoryPid(pid); ressource+=" "+m ; } catch (FileNotFoundException e) { // Most probably the process doesn't exist anymore, // We reset the information of the process to default value Log.v(TAG,"The process is probably not running. Reset the information."); processinformation.reset(); ressource+=" 0"; }catch (Exception e) { e.printStackTrace(); return "Unexpected error trying to read the memory"; } } else{ ressource+=" 0"; } if (Integer.valueOf(Build.VERSION.SDK) >= 8) { if (isNetworkUsageByProcessOffsetSet() == false){ initNetworkUsageByProcessOffset(); } try { int uid = getPackageManager().getApplicationInfo( processinformation.getProcess_name(),0).uid; if (uid != -1) { long newTotalTx = getUidTxBytes(uid); long newTotalRx = getUidRxBytes(uid); long tx = newTotalTx - uidTxBytesOffsets.get(uid); if (tX < 0) tX = 0; long rx = newTotalRx - uidRxBytesOffsets.get(uid); if (rX < 0) rX = 0; uidTxBytesOffsets.put(uid, newTotalTx); uidRxBytesOffsets.put(uid, newTotalRx); ressource += " " + (tx); ressource += " " + (rx); } else { ressource += " -1"; ressource += " -1"; } } catch (NameNotFoundException e1) { // TODO Auto-generated catch block e1.printStackTrace(); ressource += " -1"; ressource += " -1"; } } else { Log.v(TAG,"API level < 8 : No network data "); } } else { // Process is not running String nameprocess = processinformation.getProcess_name(); ressource+="\n"+nameprocess+" -1 -1 -1 -1"; } } } notifyGlobalChange(ressource,totalmem); //Log.v(TAG,ressource); return ressource+"\nEND"; } else if (inputline.equals("DISPLAY")) { Log.v(TAG,"DISPLAY"); /* First, get the Display from the WindowManager */ Display display = ((WindowManager) getSystemService(WINDOW_SERVICE)).getDefaultDisplay(); /* Now we can retrieve all display-related informations */ int width = display.getWidth(); int height = display.getHeight(); int orientation = display.getOrientation(); return "width "+width+" height "+height+" orientation "+orientation; } else if (inputline.matches("RANDOMLIST")) { Log.v(TAG,"RANDOMLIST"); String packageList =""; PackageManager pm = this._context.getPackageManager(); List<String> mainPack = new ArrayList<String>(); Intent mainItent = new Intent(Intent.ACTION_MAIN); mainItent.addCategory(Intent.CATEGORY_LAUNCHER); List<ResolveInfo> mainApps = pm.queryIntentActivities(mainItent,0); for (int i=0; i<mainApps.size(); i++) { ResolveInfo ri = mainApps.get(i); String packName = ri.activityInfo.applicationInfo.packageName; if (!mainPack.contains(packName)) { //Log.v(TAG,"Package:"+ri.activityInfo.applicationInfo.packageName); packageList += "\n"+ri.activityInfo.applicationInfo.packageName; mainPack.add(packName); } } mainItent = new Intent(Intent.ACTION_MAIN); mainItent.addCategory(Intent.CATEGORY_MONKEY); mainApps = pm.queryIntentActivities(mainItent,0); for (int i=0; i<mainApps.size(); i++) { ResolveInfo ri = mainApps.get(i); String packName = ri.activityInfo.applicationInfo.packageName; if (!mainPack.contains(packName)) { //Log.v(TAG,"Package:"+ri.activityInfo.applicationInfo.packageName); packageList += "\n"+ri.activityInfo.applicationInfo.packageName; mainPack.add(packName); } } return mainPack.size()+packageList; }else if (inputline.matches("PROCESSLIST")) { Log.v(TAG,"PROCESSLIST"); PackageManager pm = this._context.getPackageManager(); Vector<String> processApps = new Vector<String>(); List<ApplicationInfo> pis = pm.getInstalledApplications(PackageManager.GET_ACTIVITIES | PackageManager.GET_PROVIDERS | PackageManager.GET_RECEIVERS | PackageManager.GET_SERVICES); for (int i=0; i< pis.size(); i++) { ApplicationInfo pi = pis.get(i); if (!processApps.contains(pi.processName)) processApps.add(pi.processName); } String processList =""; for (int i=0; i<processApps.size(); i++) { Log.v(TAG,"Process:"+processApps.get(i)); processList += "\n"+processApps.get(i); } return processApps.size()+processList; } else if (inputline.matches("PROCESSINFO")) { Log.v(TAG,"PROCESSINFO"); PackageManager pm = this._context.getPackageManager(); List<ApplicationInfo> pis = pm.getInstalledApplications(PackageManager.GET_ACTIVITIES | PackageManager.GET_PROVIDERS | PackageManager.GET_RECEIVERS | PackageManager.GET_SERVICES); String processInfo =""; for (int i=0; i< pis.size(); i++) { ApplicationInfo pi = pis.get(i); processInfo += "\n"+pi.processName+" "+pi.packageName; Log.v(TAG,"Process:"+pi.processName+" "+pi.packageName); } return pis.size()+processInfo; } else if (inputline.matches("ENABLEFLIGHTMODE")) { Log.v(TAG,"ENABLEFLIGHTMODE"); setFlightMode(true); return "OK"; } else if (inputline.matches("DISABLEFLIGHTMODE")) { Log.v(TAG,"DISABLEFLIGHTMODE"); setFlightMode(false); return "OK"; } else { Log.v(TAG,"Unknown command"); return "Unknown"; } } private boolean isNetworkUsageByProcessOffsetSet(){ if (uidRxBytesOffsets.isEmpty() && uidTxBytesOffsets.isEmpty()){ return false; }else { return true; } } private void initNetworkUsageByProcessOffset(){ // initialize uid network stats infos LinkedList<ProcessInformation> listprocess = cput .getListprocess(); for (Iterator<ProcessInformation> iterator = listprocess .iterator(); iterator.hasNext();) { ProcessInformation processInformation = (ProcessInformation) iterator .next(); if (processInformation.isRunning(this._context)) { try { String processName = processInformation.getProcess_name(); int uid = getPackageManager().getApplicationInfo( processName, 0).uid; long rx=getUidRxBytes(uid); long tx=getUidTxBytes(uid); uidRxBytesOffsets.put(uid, rx); uidTxBytesOffsets.put(uid, tx); // Log.v(TAG, "Process="+processName+", Rx="+rx+", Tx="+tx); } catch (NameNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } private void setFlightMode(boolean on) { boolean isFlightModeOn = Settings.System.getInt(_context.getContentResolver(),Settings.System.AIRPLANE_MODE_ON, 0) != 0; if (isFlightModeOn) Log.v(TAG,"Flight mode is on"); else Log.v(TAG,"Flight mode is off"); if(isFlightModeOn && !on) { Log.v(TAG,"Trying to set Flight mode off ..."); Settings.System.putInt(_context.getContentResolver(), Settings.System.AIRPLANE_MODE_ON, 0); Log.v(TAG,"system settings set"); Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); intent.putExtra("state", 0); _context.sendBroadcast(intent); Log.v(TAG,"intent sent to broadcast"); return; } else if(!isFlightModeOn && on) { Log.v(TAG,"Trying to set Flight mode on ..."); Settings.System.putInt(_context.getContentResolver(), Settings.System.AIRPLANE_MODE_ON, 1); Log.v(TAG,"system settings set"); Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); intent.putExtra("state", 1); _context.sendBroadcast(intent); Log.v(TAG,"intent sent to broadcast"); return; } } /** * Only valid on API >= 8 * @param uid process uid * @return transfered bytes for this uid */ private long getUidTxBytes(int uid){ String className = "android.net.TrafficStats"; String methodName = "getUidTxBytes"; try { Class<?> handler = Class.forName(className); Method m = handler.getDeclaredMethod(methodName, int.class); long result = (Long) m.invoke(handler.newInstance(),uid); return result; } catch (Exception e) { e.printStackTrace(); return -1; } } /** * Only valid on API >= 8 * @param uid process uid * @return received bytes for this uid */ private long getUidRxBytes(int uid){ String className = "android.net.TrafficStats"; String methodName = "getUidRxBytes"; try { Class<?> handler = Class.forName(className); Method m = handler.getDeclaredMethod(methodName, int.class); long result = (Long) m.invoke(handler.newInstance(),uid); return result; } catch (Exception e) { e.printStackTrace(); return -1; } } /** * Only valid on API >= 8 * @return */ private long getTotalRxBytes() { String className = "android.net.TrafficStats"; String methodName = "getTotalRxBytes"; try { Class<?> handler = Class.forName(className); Method m = handler.getDeclaredMethod(methodName, (Class[]) null); long result = (Long) m.invoke(handler.newInstance()); return result; } catch (Exception e) { e.printStackTrace(); return -1; } } /** * Only valid on API >= 8 * @return */ private long getTotalTxBytes() { String className = "android.net.TrafficStats"; String methodName = "getTotalTxBytes"; try { Class<?> handler = Class.forName(className); Method m = handler.getDeclaredMethod(methodName, (Class[]) null); long result = (Long) m.invoke(handler.newInstance()); return result; } catch (Exception e) { e.printStackTrace(); return -1; } } private String getUsedMemoryPid(int pid) throws IOException { int x = Integer.valueOf(Build.VERSION.SDK); if ( x >= 6) { try { Log.v(TAG,"getUsedMemoryPid("+pid+")"); int [] pids = {pid}; ActivityManager am = (ActivityManager) getSystemService("activity"); // Method is invocated dynamically, because on 1.5 platforms it is not available Method[] methods = am.getClass().getMethods(); for (Method method : methods) { if(method.getName().equals("getProcessMemoryInfo")) { Debug.MemoryInfo [] meminfo = (MemoryInfo[]) method.invoke(am, pids); Method m = meminfo[0].getClass().getMethod("getTotalPrivateDirty",(Class[])null); String mem = Integer.toString((Integer)m.invoke(meminfo[0], (Object[])null)); //Some phones need the application to be sign //we use the old method for those applications if(mem.equals("0")){ FileReader fileReader = new FileReader("/proc/"+pid+"/status"); BufferedReader in = new BufferedReader( fileReader, 50); String ret = null; //Skip the first 14 lines for(int j=0; j<15; j++) ret = in.readLine(); fileReader.close(); //VmRSS = Virtual Memory Resident Stack Size //Taille de la m�moire physique utilis�e. mem = ret.split(" +")[1]; Log.v(TAG,"mem (1.5 VmRSS) = "+m); } else Log.v(TAG,"mem (2.1 TotalPrivateDirty) = "+mem); return mem; } } return "0"; } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); return "ActivityManager problem"; } } else if (x< 6){ FileReader fileReader = new FileReader("/proc/"+pid+"/status"); BufferedReader in = new BufferedReader( fileReader, 50); String ret = null; //Skip the first 14 lines for(int j=0; j<15; j++) ret = in.readLine(); fileReader.close(); String m = ret.split(" +")[1]; //VmRSS = Virtual Memory Resident Stack Size //Taille de la m�moire physique utilis�e. Log.v(TAG,"mem (1.5 VmRSS) = "+m); return m; } else return "-1"; } private String getTotalMemoryUsed() { FileReader fileReader; //Get memory used try { fileReader = new FileReader("/proc/meminfo"); BufferedReader in = new BufferedReader( fileReader, 50); String [] x = in.readLine().split(" +"); String [] y = in.readLine().split(" +"); fileReader.close(); totalmem = x[1]; int memused = Integer.parseInt(x[1])-Integer.parseInt(y[1]); fileReader.close(); return String.valueOf(memused); } catch (Exception e1) { // TODO Auto-generated catch block e1.printStackTrace(); return "Error reading memory"; } } public String getCpu() { return cput.getCPU()+""; } public String getConnection() { return null; } public String getMem() { // TODO Auto-generated method stub return null; } private void batteryLevel() { //BatteryManager battery = (BatteryManager) _context.getSystemService(Context.POWER_SERVICE); BroadcastReceiver batteryLevelReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { int rawlevel = intent.getIntExtra("level", -1); int scale = intent.getIntExtra("scale", -1); if (rawlevel >= 0 && scale > 0) { batteryLevel = (rawlevel * 100) / scale; } Log.v(TAG,"Battery Level: " + batteryLevel + "%"); } }; IntentFilter batteryLevelFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); _context.registerReceiver(batteryLevelReceiver, batteryLevelFilter); } /** * * @param NameProcess * @return The PID of the process (return -1 if the process was not found) */ public int getPidFromName(String NameProcess){ ActivityManager activityManager = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE); List<RunningAppProcessInfo> procInfos = activityManager.getRunningAppProcesses(); int PIDfound = -1; for(int i = 0; i < procInfos.size(); i++) { if(NameProcess.equals(procInfos.get(i).processName)){ PIDfound = procInfos.get(i).pid; break; } } Log.v(TAG,"getPidFromName for process "+NameProcess+". PID="+PIDfound); return PIDfound; } @Override public void onCreate() { Log.i(TAG,"onCreate"); super.onCreate(); } @Override public void onDestroy() { Log.i(TAG,"onDestroy"); super.onDestroy(); } @Override public void onLowMemory() { Log.i(TAG,"onLowMemory"); super.onLowMemory(); } @Override public void onRebind(Intent intent) { Log.i(TAG,"onRebind"); super.onRebind(intent); } @Override public boolean onUnbind(Intent intent) { Log.i(TAG,"onUnbind"); return super.onUnbind(intent); } @Override void initBeforeLoop() { // We clear the process list to be sure nothing is left cput = new CPUThread(1000); t = new Thread(cput); t.setName("ATKMonitorCPUThread"); t.start(); } @Override void releaseAfterLoop(){ if (cput != null) cput.quit(); } @Override int getPort() { return PORT; } }