FileDocCategorySizeDatePackage
UsageStatsService.javaAPI DocAndroid 1.5 API18322Wed May 06 22:42:00 BST 2009com.android.server.am

UsageStatsService

public final class UsageStatsService extends IUsageStats.Stub
This service collects the statistics associated with usage of various components, like when a particular package is launched or paused and aggregates events like number of time a component is launched total duration of a component launch.

Fields Summary
public static final String
SERVICE_NAME
private static final boolean
localLOGV
private static final String
TAG
static com.android.internal.app.IUsageStats
sService
private android.content.Context
mContext
private final Map
mStats
final Object
mStatsLock
final Object
mFileLock
private String
mResumedPkg
private File
mFile
private long
mLastWriteRealTime
private int
_FILE_WRITE_INTERVAL
private static final String
_PREFIX_DELIMIT
private String
mFilePrefix
private Calendar
mCal
private static final int
_MAX_NUM_FILES
private long
mLastTime
Constructors Summary
UsageStatsService(String fileName)

        mStats = new HashMap<String, PkgUsageStatsExtended>();
        mStatsLock = new Object();
        mFileLock = new Object();
        mFilePrefix = fileName;
        mCal = Calendar.getInstance();
        // Update current stats which are binned by date
        String uFileName = getCurrentDateStr(mFilePrefix);
        mFile = new File(uFileName);
        readStatsFromFile();
        mLastWriteRealTime = SystemClock.elapsedRealtime();
        mLastTime = new Date().getTime();
    
Methods Summary
private voidcheckFileLimitFLOCK()

        File dir = getUsageFilesDir();
        if (dir == null) {
            Log.w(TAG, "Couldnt find writable directory for usage stats file");
            return;
        }
        // Get all usage stats output files
        ArrayList<String> fileList = getUsageStatsFileListFLOCK();
        if (fileList == null) {
            // Strange but we dont have to delete any thing
            return;
        }
        int count = fileList.size();
        if (count <= _MAX_NUM_FILES) {
            return;
        }
        // Sort files
        Collections.sort(fileList);
        count -= _MAX_NUM_FILES;
        // Delete older files
        for (int i = 0; i < count; i++) {
            String fileName = fileList.get(i);
            File file = new File(dir, fileName);
            Log.i(TAG, "Deleting file : "+fileName);
            file.delete();
        }
    
private voidcollectDumpInfoFLOCK(java.io.PrintWriter pw, java.lang.String[] args)

        List<String> fileList = getUsageStatsFileListFLOCK();
        if (fileList == null) {
            return;
        }
        final boolean isCheckinRequest = scanArgs(args, "-c");
        Collections.sort(fileList);
        File usageFile = new File(mFilePrefix);
        String dirName = usageFile.getParent();
        File dir = new File(dirName);
        String filePrefix = usageFile.getName();
        // file name followed by dot
        int prefixLen = filePrefix.length()+1;
        String todayStr = getCurrentDateStr(null);
        for (String file : fileList) {
            File dFile = new File(dir, file);
            String dateStr = file.substring(prefixLen);
            try {
                Parcel in = getParcelForFile(dFile);
                collectDumpInfoFromParcelFLOCK(in, pw, dateStr, isCheckinRequest);
                if (isCheckinRequest && !todayStr.equalsIgnoreCase(dateStr)) {
                    // Delete old file after collecting info only for checkin requests
                    dFile.delete();
                }
            } catch (FileNotFoundException e) {
                Log.w(TAG, "Failed with "+e+" when collecting dump info from file : " + file);
                return;
            } catch (IOException e) {
                Log.w(TAG, "Failed with "+e+" when collecting dump info from file : "+file);
            }      
        }
    
private voidcollectDumpInfoFromParcelFLOCK(android.os.Parcel in, java.io.PrintWriter pw, java.lang.String date, boolean isCheckinRequest)

        StringBuilder sb = new StringBuilder();
        sb.append("Date:");
        sb.append(date);
        boolean first = true;
        while (in.dataAvail() > 0) {
            String pkgName = in.readString();
            int launchCount = in.readInt();
            long usageTime = in.readLong();
            if (isCheckinRequest) {
                if (!first) {
                    sb.append(",");
                }
                sb.append(pkgName);
                sb.append(",");
                sb.append(launchCount);
                sb.append(",");
                sb.append(usageTime);
                sb.append("ms");
            } else {
                if (first) {
                    sb.append("\n");
                }
                sb.append("pkg=");
                sb.append(pkgName);
                sb.append(", launchCount=");
                sb.append(launchCount);
                sb.append(", usageTime=");
                sb.append(usageTime);
                sb.append(" ms\n");
            }
            first = false;
        }
        pw.write(sb.toString());
    
protected voiddump(java.io.FileDescriptor fd, java.io.PrintWriter pw, java.lang.String[] args)

        synchronized (mFileLock) {
            collectDumpInfoFLOCK(pw, args);
        }
    
public voidenforceCallingPermission()

        if (Binder.getCallingPid() == Process.myPid()) {
            return;
        }
        mContext.enforcePermission(android.Manifest.permission.UPDATE_DEVICE_STATS,
                Binder.getCallingPid(), Binder.getCallingUid(), null);
    
public com.android.internal.os.PkgUsageStats[]getAllPkgUsageStats()

        mContext.enforceCallingOrSelfPermission(
                android.Manifest.permission.PACKAGE_USAGE_STATS, null);
        synchronized (mStatsLock) {
            Set<String> keys = mStats.keySet();
            int size = keys.size();
            if (size <= 0) {
                return null;
            }
            PkgUsageStats retArr[] = new PkgUsageStats[size];
            int i = 0;
            for (String key: keys) {
                PkgUsageStatsExtended pus = mStats.get(key);
                retArr[i] = new PkgUsageStats(key, pus.mLaunchCount, pus.mUsageTime);
                i++;
            }
            return retArr;
        }
    
private java.lang.StringgetCurrentDateStr(java.lang.String prefix)

        mCal.setTime(new Date());
        StringBuilder sb = new StringBuilder();
        if (prefix != null) {
            sb.append(prefix);
            sb.append(".");
        }
        int mm = mCal.get(Calendar.MONTH) - Calendar.JANUARY +1;
        if (mm < 10) {
            sb.append("0");
        }
        sb.append(mm);
        int dd = mCal.get(Calendar.DAY_OF_MONTH);
        if (dd < 10) {
            sb.append("0");
        }
        sb.append(dd);
        sb.append(mCal.get(Calendar.YEAR));
        return sb.toString();
    
private android.os.ParcelgetParcelForFile(java.io.File file)

        FileInputStream stream = new FileInputStream(file);
        byte[] raw = readFully(stream);
        Parcel in = Parcel.obtain();
        in.unmarshall(raw, 0, raw.length);
        in.setDataPosition(0);
        stream.close();
        return in;
    
public com.android.internal.os.PkgUsageStatsgetPkgUsageStats(android.content.ComponentName componentName)

        mContext.enforceCallingOrSelfPermission(
                android.Manifest.permission.PACKAGE_USAGE_STATS, null);
        String pkgName;
        if ((componentName == null) ||
                ((pkgName = componentName.getPackageName()) == null)) {
            return null;
        }
        synchronized (mStatsLock) {
            PkgUsageStatsExtended pus = mStats.get(pkgName);
            if (pus == null) {
               return null;
            }
            return new PkgUsageStats(pkgName, pus.mLaunchCount, pus.mUsageTime);
        }
    
public static com.android.internal.app.IUsageStatsgetService()

        if (sService != null) {
            return sService;
        }
        IBinder b = ServiceManager.getService(SERVICE_NAME);
        sService = asInterface(b);
        return sService;
    
private java.io.FilegetUsageFilesDir()

        if (mFilePrefix == null) {
            return null;
        }
        File pre = new File(mFilePrefix);
        return new File(pre.getParent());
    
private java.util.ArrayListgetUsageStatsFileListFLOCK()

        File dir = getUsageFilesDir();
        if (dir == null) {
            Log.w(TAG, "Couldnt find writable directory for usage stats file");
            return null;
        }
        // Check if there are too many files in the system and delete older files
        String fList[] = dir.list();
        if (fList == null) {
            return null;
        }
        File pre = new File(mFilePrefix);
        String filePrefix = pre.getName();
        // file name followed by dot
        int prefixLen = filePrefix.length()+1;
        ArrayList<String> fileList = new ArrayList<String>();
        for (String file : fList) {
            int index = file.indexOf(filePrefix);
            if (index == -1) {
                continue;
            }
            if (file.endsWith(".bak")) {
                continue;
            }
            fileList.add(file);
        }
        return fileList;
    
public voidnotePauseComponent(android.content.ComponentName componentName)

        enforceCallingPermission();
        String pkgName;
        if ((componentName == null) ||
                ((pkgName = componentName.getPackageName()) == null)) {
            return;
        }
        if ((mResumedPkg == null) || (!pkgName.equalsIgnoreCase(mResumedPkg))) {
            Log.w(TAG, "Something wrong here, Didn't expect "+pkgName+" to be paused");
            return;
        }
        if (localLOGV) Log.i(TAG, "paused component:"+pkgName);
        synchronized (mStatsLock) {
            PkgUsageStatsExtended pus = mStats.get(pkgName);
            if (pus == null) {
                // Weird some error here
                Log.w(TAG, "No package stats for pkg:"+pkgName);
                return;
            }
            pus.updatePause();
        }
        // Persist data to file
        writeStatsToFile();
    
public voidnoteResumeComponent(android.content.ComponentName componentName)

        enforceCallingPermission();
        String pkgName;
        if ((componentName == null) ||
                ((pkgName = componentName.getPackageName()) == null)) {
            return;
        }
        if ((mResumedPkg != null) && (mResumedPkg.equalsIgnoreCase(pkgName))) {
            // Moving across activities in same package. just return
            return;
        } 
        if (localLOGV) Log.i(TAG, "started component:"+pkgName);
        synchronized (mStatsLock) {
            PkgUsageStatsExtended pus = mStats.get(pkgName);
            if (pus == null) {
                pus = new PkgUsageStatsExtended();
                mStats.put(pkgName, pus);
            }
            pus.updateResume();
        }
        mResumedPkg = pkgName;
    
public voidpublish(android.content.Context context)

        mContext = context;
        ServiceManager.addService(SERVICE_NAME, asBinder());
    
static byte[]readFully(java.io.FileInputStream stream)

        int pos = 0;
        int avail = stream.available();
        byte[] data = new byte[avail];
        while (true) {
            int amt = stream.read(data, pos, data.length-pos);
            if (amt <= 0) {
                return data;
            }
            pos += amt;
            avail = stream.available();
            if (avail > data.length-pos) {
                byte[] newData = new byte[pos+avail];
                System.arraycopy(data, 0, newData, 0, pos);
                data = newData;
            }
        }
    
private voidreadStatsFLOCK(java.io.File file)

        Parcel in = getParcelForFile(file);
        while (in.dataAvail() > 0) {
            String pkgName = in.readString();
            PkgUsageStatsExtended pus = new PkgUsageStatsExtended();
            pus.mLaunchCount = in.readInt();
            pus.mUsageTime = in.readLong();
            synchronized (mStatsLock) {
                mStats.put(pkgName, pus);
            }
        }
    
private voidreadStatsFromFile()

        File newFile = mFile;
        synchronized (mFileLock) {
            try {
                if (newFile.exists()) {
                    readStatsFLOCK(newFile);
                } else {
                    // Check for file limit before creating a new file
                    checkFileLimitFLOCK();
                    newFile.createNewFile();
                }
            } catch (IOException e) {
                Log.w(TAG,"Error : " + e + " reading data from file:" + newFile);
            }
        }
    
private static booleanscanArgs(java.lang.String[] args, java.lang.String value)
Searches array of arguments for the specified string

param
args array of argument strings
param
value value to search for
return
true if the value is contained in the array

        if (args != null) {
            for (String arg : args) {
                if (value.equals(arg)) {
                    return true;
                }
            }
        }
        return false;
    
private voidwriteStatsFLOCK()

        FileOutputStream stream = new FileOutputStream(mFile);
        Parcel out = Parcel.obtain();
        writeStatsToParcelFLOCK(out);
        stream.write(out.marshall());
        out.recycle();
        stream.flush();
        stream.close();
    
private voidwriteStatsToFile()

        synchronized (mFileLock) {
            long currTime = new Date().getTime();
            boolean dayChanged =  ((currTime - mLastTime) >= (24*60*60*1000));
            long currRealTime = SystemClock.elapsedRealtime();
            if (((currRealTime-mLastWriteRealTime) < _FILE_WRITE_INTERVAL) &&
                    (!dayChanged)) {
                // wait till the next update
                return;
            }
            // Get the most recent file
            String todayStr = getCurrentDateStr(mFilePrefix);
            // Copy current file to back up
            File backupFile =  new File(mFile.getPath() + ".bak");
            mFile.renameTo(backupFile);
            try {
                checkFileLimitFLOCK();
                mFile.createNewFile();
                // Write mStats to file
                writeStatsFLOCK();
                mLastWriteRealTime = currRealTime;
                mLastTime = currTime;
                if (dayChanged) {
                    // clear stats
                    synchronized (mStats) {
                        mStats.clear();
                    }
                    mFile = new File(todayStr);
                }
                // Delete the backup file
                if (backupFile != null) {
                    backupFile.delete();
                }
            } catch (IOException e) {
                Log.w(TAG, "Failed writing stats to file:" + mFile);
                if (backupFile != null) {
                    backupFile.renameTo(mFile);
                }
            }
        }
    
private voidwriteStatsToParcelFLOCK(android.os.Parcel out)

        synchronized (mStatsLock) {
            Set<String> keys = mStats.keySet();
            for (String key : keys) {
                PkgUsageStatsExtended pus = mStats.get(key);
                out.writeString(key);
                out.writeInt(pus.mLaunchCount);
                out.writeLong(pus.mUsageTime);
            }
        }