Add reasons to notifyPackageUse calls

This is so we can record more specific times in PackageUsage.
If file with only one timestamp per package is found, the value is
copied to all usage slots.

Bug: 27902702
Change-Id: I8affe43c735e54620a9204433aad367cfddfded7
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 5b8d98c..b075279 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -711,6 +711,8 @@
                         updateServiceForegroundLocked(r.app, true);
                     }
                     getServiceMap(r.userId).ensureNotStartingBackground(r);
+                    mAm.notifyPackageUse(r.serviceInfo.packageName,
+                                         PackageManager.NOTIFY_PACKAGE_USE_FOREGROUND_SERVICE);
                 } else {
                     if (r.isForeground) {
                         r.isForeground = false;
@@ -1756,7 +1758,8 @@
             synchronized (r.stats.getBatteryStats()) {
                 r.stats.startLaunchedLocked();
             }
-            mAm.notifyPackageUse(r.serviceInfo.packageName);
+            mAm.notifyPackageUse(r.serviceInfo.packageName,
+                                 PackageManager.NOTIFY_PACKAGE_USE_SERVICE);
             app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
             app.thread.scheduleCreateService(r, r.serviceInfo,
                     mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 3b024cf..5b51954 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3350,10 +3350,10 @@
         return proc;
     }
 
-    void notifyPackageUse(String packageName) {
+    void notifyPackageUse(String packageName, int reason) {
         IPackageManager pm = AppGlobals.getPackageManager();
         try {
-            pm.notifyPackageUse(packageName);
+            pm.notifyPackageUse(packageName, reason);
         } catch (RemoteException e) {
         }
     }
@@ -6363,11 +6363,9 @@
                                 || (mBackupTarget.backupMode == BackupRecord.BACKUP_FULL));
             }
 
-            notifyPackageUse(app.instrumentationInfo != null
-                    ? app.instrumentationInfo.packageName
-                    : app.info.packageName);
             if (app.instrumentationClass != null) {
-                notifyPackageUse(app.instrumentationClass.getPackageName());
+                notifyPackageUse(app.instrumentationClass.getPackageName(),
+                                 PackageManager.NOTIFY_PACKAGE_USE_INSTRUMENTATION);
             }
             if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Binding proc "
                     + processName + " with config " + mConfiguration);
@@ -6447,7 +6445,8 @@
         if (!badApp && mBackupTarget != null && mBackupTarget.appInfo.uid == app.uid) {
             if (DEBUG_BACKUP) Slog.v(TAG_BACKUP,
                     "New app is backup target, launching agent for " + app);
-            notifyPackageUse(mBackupTarget.appInfo.packageName);
+            notifyPackageUse(mBackupTarget.appInfo.packageName,
+                             PackageManager.NOTIFY_PACKAGE_USE_BACKUP);
             try {
                 thread.scheduleCreateBackupAgent(mBackupTarget.appInfo,
                         compatibilityInfoForPackageLocked(mBackupTarget.appInfo),
@@ -10094,7 +10093,8 @@
                     app.addPackage(cpi.applicationInfo.packageName, cpi.applicationInfo.versionCode,
                             mProcessStats);
                 }
-                notifyPackageUse(cpi.applicationInfo.packageName);
+                notifyPackageUse(cpi.applicationInfo.packageName,
+                                 PackageManager.NOTIFY_PACKAGE_USE_CONTENT_PROVIDER);
             }
         }
         return providers;
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 97ee744..f285cea 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1215,7 +1215,8 @@
                 // Home process is the root process of the task.
                 mService.mHomeProcess = task.mActivities.get(0).app;
             }
-            mService.notifyPackageUse(r.intent.getComponent().getPackageName());
+            mService.notifyPackageUse(r.intent.getComponent().getPackageName(),
+                                      PackageManager.NOTIFY_PACKAGE_USE_ACTIVITY);
             r.sleeping = false;
             r.forceNewConfig = false;
             mService.showAskCompatModeDialogLocked(r);
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index bbb8bdf..1222f54 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -274,7 +274,8 @@
             if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
                     "Delivering to component " + r.curComponent
                     + ": " + r);
-            mService.notifyPackageUse(r.intent.getComponent().getPackageName());
+            mService.notifyPackageUse(r.intent.getComponent().getPackageName(),
+                                      PackageManager.NOTIFY_PACKAGE_USE_BROADCAST_RECEIVER);
             app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
                     mService.compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo),
                     r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 0633625..f285dec 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1152,16 +1152,24 @@
                     try {
                         f = file.startWrite();
                         BufferedOutputStream out = new BufferedOutputStream(f);
-                        FileUtils.setPermissions(file.getBaseFile().getPath(), 0640, SYSTEM_UID, PACKAGE_INFO_GID);
+                        FileUtils.setPermissions(file.getBaseFile().getPath(),
+                                0640, SYSTEM_UID, PACKAGE_INFO_GID);
                         StringBuilder sb = new StringBuilder();
+
+                        sb.append(USAGE_FILE_MAGIC_VERSION_1);
+                        sb.append('\n');
+                        out.write(sb.toString().getBytes(StandardCharsets.US_ASCII));
+
                         for (PackageParser.Package pkg : mPackages.values()) {
-                            if (pkg.mLastPackageUsageTimeInMills == 0) {
+                            if (pkg.getLatestPackageUseTimeInMills() == 0L) {
                                 continue;
                             }
                             sb.setLength(0);
                             sb.append(pkg.packageName);
-                            sb.append(' ');
-                            sb.append((long)pkg.mLastPackageUsageTimeInMills);
+                            for (long usageTimeInMillis : pkg.mLastPackageUsageTimeInMills) {
+                                sb.append(' ');
+                                sb.append(usageTimeInMillis);
+                            }
                             sb.append('\n');
                             out.write(sb.toString().getBytes(StandardCharsets.US_ASCII));
                         }
@@ -1185,28 +1193,12 @@
                 try {
                     in = new BufferedInputStream(file.openRead());
                     StringBuffer sb = new StringBuffer();
-                    while (true) {
-                        String packageName = readToken(in, sb, ' ');
-                        if (packageName == null) {
-                            break;
-                        }
-                        String timeInMillisString = readToken(in, sb, '\n');
-                        if (timeInMillisString == null) {
-                            throw new IOException("Failed to find last usage time for package "
-                                                  + packageName);
-                        }
-                        PackageParser.Package pkg = mPackages.get(packageName);
-                        if (pkg == null) {
-                            continue;
-                        }
-                        long timeInMillis;
-                        try {
-                            timeInMillis = Long.parseLong(timeInMillisString);
-                        } catch (NumberFormatException e) {
-                            throw new IOException("Failed to parse " + timeInMillisString
-                                                  + " as a long.", e);
-                        }
-                        pkg.mLastPackageUsageTimeInMills = timeInMillis;
+
+                    String firstLine = readLine(in, sb);
+                    if (firstLine.equals(USAGE_FILE_MAGIC_VERSION_1)) {
+                        readVersion1LP(in, sb);
+                    } else {
+                        readVersion0LP(in, sb, firstLine);
                     }
                 } catch (FileNotFoundException expected) {
                     mIsHistoricalPackageUsageAvailable = false;
@@ -1219,6 +1211,76 @@
             mLastWritten.set(SystemClock.elapsedRealtime());
         }
 
+        private void readVersion0LP(InputStream in, StringBuffer sb, String firstLine)
+                throws IOException {
+            // Initial version of the file had no version number and stored one
+            // package-timestamp pair per line.
+            // Note that the first line has already been read from the InputStream.
+            String line = firstLine;
+            while (true) {
+                if (line == null) {
+                    break;
+                }
+
+                String[] tokens = line.split(" ");
+                if (tokens.length != 2) {
+                    throw new IOException("Failed to parse " + line +
+                            " as package-timestamp pair.");
+                }
+
+                String packageName = tokens[0];
+                PackageParser.Package pkg = mPackages.get(packageName);
+                if (pkg == null) {
+                    continue;
+                }
+
+                long timestamp = parseAsLong(tokens[1]);
+                for (int reason = 0;
+                        reason < PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT;
+                        reason++) {
+                    pkg.mLastPackageUsageTimeInMills[reason] = timestamp;
+                }
+
+                line = readLine(in, sb);
+            }
+        }
+
+        private void readVersion1LP(InputStream in, StringBuffer sb) throws IOException {
+            // Version 1 of the file started with the corresponding version
+            // number and then stored a package name and eight timestamps per line.
+            String line;
+            while ((line = readLine(in, sb)) != null) {
+                String[] tokens = line.split(" ");
+                if (tokens.length != PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT + 1) {
+                    throw new IOException("Failed to parse " + line + " as a timestamp array.");
+                }
+
+                String packageName = tokens[0];
+                PackageParser.Package pkg = mPackages.get(packageName);
+                if (pkg == null) {
+                    continue;
+                }
+
+                for (int reason = 0;
+                        reason < PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT;
+                        reason++) {
+                    pkg.mLastPackageUsageTimeInMills[reason] = parseAsLong(tokens[reason + 1]);
+                }
+            }
+        }
+
+        private long parseAsLong(String token) throws IOException {
+            try {
+                return Long.parseLong(token);
+            } catch (NumberFormatException e) {
+                throw new IOException("Failed to parse " + token + " as a long.", e);
+            }
+        }
+
+        private String readLine(InputStream in, StringBuffer sb) throws IOException {
+            return readToken(in, sb, '\n');
+        }
+
         private String readToken(InputStream in, StringBuffer sb, char endOfToken)
                 throws IOException {
             sb.setLength(0);
@@ -1243,6 +1305,9 @@
             File fname = new File(systemDir, "package-usage.list");
             return new AtomicFile(fname);
         }
+
+        private static final String USAGE_FILE_MAGIC = "PACKAGE_USAGE__VERSION_";
+        private static final String USAGE_FILE_MAGIC_VERSION_1 = USAGE_FILE_MAGIC + "1";
     }
 
     class PackageHandler extends Handler {
@@ -7130,13 +7195,13 @@
     }
 
     @Override
-    public void notifyPackageUse(String packageName) {
+    public void notifyPackageUse(String packageName, int reason) {
         synchronized (mPackages) {
             PackageParser.Package p = mPackages.get(packageName);
             if (p == null) {
                 return;
             }
-            p.mLastPackageUsageTimeInMills = System.currentTimeMillis();
+            p.mLastPackageUsageTimeInMills[reason] = System.currentTimeMillis();
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index f79d6ee..3c065ae 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -69,7 +69,7 @@
         long now = System.currentTimeMillis();
         for (Iterator<PackageParser.Package> i = pkgs.iterator(); i.hasNext();) {
             PackageParser.Package pkg = i.next();
-            long then = pkg.mLastPackageUsageTimeInMills;
+            long then = pkg.getLatestPackageUseTimeInMills();
             if (then + dexOptLRUThresholdInMills < now) {
                 if (DEBUG_DEXOPT) {
                     Log.i(TAG, "Skipping dexopt of " + pkg.packageName + " last resumed: " +