Fix issue #1924851

We now only increment the launch count when we are launching from one package
to another.  Also the individual components in a package now have a count
of the number of times they have been entered, which likewise is only updated
when going to one component from another.

This requires a new data format (all old data is wiped) and new checkin
dump format (tools must be updated to read it).
diff --git a/services/java/com/android/server/am/UsageStatsService.java b/services/java/com/android/server/am/UsageStatsService.java
index 866334b..2d58659 100755
--- a/services/java/com/android/server/am/UsageStatsService.java
+++ b/services/java/com/android/server/am/UsageStatsService.java
@@ -56,9 +56,9 @@
     private static final String TAG = "UsageStats";
     
     // Current on-disk Parcel version
-    private static final int VERSION = 1004;
+    private static final int VERSION = 1005;
 
-    private static final int CHECKIN_VERSION = 3;
+    private static final int CHECKIN_VERSION = 4;
     
     private static final String FILE_PREFIX = "usage-";
     
@@ -82,7 +82,9 @@
     // this lock held.
     final Object mFileLock;
     // Order of locks is mFileLock followed by mStatsLock to avoid deadlocks
-    private String mResumedPkg;
+    private String mLastResumedPkg;
+    private String mLastResumedComp;
+    private boolean mIsResumed;
     private File mFile;
     private String mFileLeaf;
     //private File mBackupFile;
@@ -92,11 +94,16 @@
     private int mLastWriteDay;
     
     static class TimeStats {
+        int count;
         int[] times = new int[NUM_LAUNCH_TIME_BINS];
         
         TimeStats() {
         }
         
+        void incCount() {
+            count++;
+        }
+        
         void add(int val) {
             final int[] bins = LAUNCH_TIME_BINS;
             for (int i=0; i<NUM_LAUNCH_TIME_BINS-1; i++) {
@@ -109,6 +116,7 @@
         }
         
         TimeStats(Parcel in) {
+            count = in.readInt();
             final int[] localTimes = times;
             for (int i=0; i<NUM_LAUNCH_TIME_BINS; i++) {
                 localTimes[i] = in.readInt();
@@ -116,6 +124,7 @@
         }
         
         void writeToParcel(Parcel out) {
+            out.writeInt(count);
             final int[] localTimes = times;
             for (int i=0; i<NUM_LAUNCH_TIME_BINS; i++) {
                 out.writeInt(localTimes[i]);
@@ -152,8 +161,10 @@
             }
         }
         
-        void updateResume() {
-            mLaunchCount ++;
+        void updateResume(boolean launched) {
+            if (launched) {
+                mLaunchCount ++;
+            }
             mResumedTime = SystemClock.elapsedRealtime();
         }
         
@@ -162,6 +173,15 @@
             mUsageTime += (mPausedTime - mResumedTime);
         }
         
+        void addLaunchCount(String comp) {
+            TimeStats times = mLaunchTimes.get(comp);
+            if (times == null) {
+                times = new TimeStats();
+                mLaunchTimes.put(comp, times);
+            }
+            times.incCount();
+        }
+        
         void addLaunchTime(String comp, int millis) {
             TimeStats times = mLaunchTimes.get(comp);
             if (times == null) {
@@ -436,43 +456,70 @@
     public void noteResumeComponent(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) {
+            if ((componentName == null) ||
+                    ((pkgName = componentName.getPackageName()) == null)) {
+                return;
+            }
+            
+            final boolean samePackage = pkgName.equals(mLastResumedPkg);
+            if (mIsResumed) {
+                if (samePackage) {
+                    Log.w(TAG, "Something wrong here, didn't expect "
+                            + pkgName + " to be resumed");
+                    return;
+                }
+                
+                if (mLastResumedPkg != null) {
+                    // We last resumed some other package...  just pause it now
+                    // to recover.
+                    Log.w(TAG, "Unexpected resume of " + pkgName
+                            + " while already resumed in " + mLastResumedPkg);
+                    PkgUsageStatsExtended pus = mStats.get(mLastResumedPkg);
+                    if (pus != null) {
+                        pus.updatePause();
+                    }
+                }
+            }
+            
+            final boolean sameComp = samePackage
+                    && componentName.getClassName().equals(mLastResumedComp);
+            
+            mIsResumed = true;
+            mLastResumedPkg = pkgName;
+            mLastResumedComp = componentName.getClassName();
+            
+            if (localLOGV) Log.i(TAG, "started component:" + pkgName);
             PkgUsageStatsExtended pus = mStats.get(pkgName);
             if (pus == null) {
                 pus = new PkgUsageStatsExtended();
                 mStats.put(pkgName, pus);
             }
-            pus.updateResume();
+            pus.updateResume(!samePackage);
+            if (!sameComp) {
+                pus.addLaunchCount(mLastResumedComp);
+            }
         }
-        mResumedPkg = pkgName;
     }
 
     public void notePauseComponent(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);
-        
-        // Persist current data to file if needed.
-        writeStatsToFile(false);
         
         synchronized (mStatsLock) {
+            String pkgName;
+            if ((componentName == null) ||
+                    ((pkgName = componentName.getPackageName()) == null)) {
+                return;
+            }
+            if (!mIsResumed) {
+                Log.w(TAG, "Something wrong here, didn't expect "
+                        + pkgName + " to be paused");
+                return;
+            }
+            mIsResumed = false;
+            
+            if (localLOGV) Log.i(TAG, "paused component:"+pkgName);
+        
             PkgUsageStatsExtended pus = mStats.get(pkgName);
             if (pus == null) {
                 // Weird some error here
@@ -481,6 +528,9 @@
             }
             pus.updatePause();
         }
+        
+        // Persist current data to file if needed.
+        writeStatsToFile(false);
     }
     
     public void noteLaunchTime(ComponentName componentName, int millis) {
@@ -631,9 +681,9 @@
             if (isCompactOutput) {
                 sb.append("P:");
                 sb.append(pkgName);
-                sb.append(",");
+                sb.append(',');
                 sb.append(pus.mLaunchCount);
-                sb.append(",");
+                sb.append(',');
                 sb.append(pus.mUsageTime);
                 sb.append('\n');
                 final int NC = pus.mLaunchTimes.size();
@@ -642,6 +692,8 @@
                         sb.append("A:");
                         sb.append(ent.getKey());
                         TimeStats times = ent.getValue();
+                        sb.append(',');
+                        sb.append(times.count);
                         for (int i=0; i<NUM_LAUNCH_TIME_BINS; i++) {
                             sb.append(",");
                             sb.append(times.times[i]);
@@ -665,25 +717,26 @@
                         sb.append("    ");
                         sb.append(ent.getKey());
                         TimeStats times = ent.getValue();
+                        sb.append(": ");
+                        sb.append(times.count);
+                        sb.append(" starts");
                         int lastBin = 0;
-                        boolean first = true;
                         for (int i=0; i<NUM_LAUNCH_TIME_BINS-1; i++) {
                             if (times.times[i] != 0) {
-                                sb.append(first ? ": " : ", ");
+                                sb.append(", ");
                                 sb.append(lastBin);
                                 sb.append('-');
                                 sb.append(LAUNCH_TIME_BINS[i]);
-                                sb.append('=');
+                                sb.append("ms=");
                                 sb.append(times.times[i]);
-                                first = false;
                             }
                             lastBin = LAUNCH_TIME_BINS[i];
                         }
                         if (times.times[NUM_LAUNCH_TIME_BINS-1] != 0) {
-                            sb.append(first ? ": " : ", ");
+                            sb.append(", ");
                             sb.append(">=");
                             sb.append(lastBin);
-                            sb.append('=');
+                            sb.append("ms=");
                             sb.append(times.times[NUM_LAUNCH_TIME_BINS-1]);
                         }
                         sb.append('\n');