Get UsageStats if no PackageUsage is available for boot dexopt filtering.

Bug: 17191977
Change-Id: I33e18459e49afa42b8e8218574a2434e5205a6da
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b79e157..6b046f2 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -84,6 +84,8 @@
 import android.app.IActivityManager;
 import android.app.admin.IDevicePolicyManager;
 import android.app.backup.IBackupManager;
+import android.app.usage.UsageStats;
+import android.app.usage.UsageStatsManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -329,6 +331,7 @@
     final boolean mFactoryTest;
     final boolean mOnlyCore;
     final boolean mLazyDexOpt;
+    final long mDexOptLRUThresholdInMills;
     final DisplayMetrics mMetrics;
     final int mDefParseFlags;
     final String[] mSeparateProcesses;
@@ -1294,6 +1297,15 @@
         mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,
                 ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);
 
+        // TODO: add a property to control this?
+        long dexOptLRUThresholdInMinutes;
+        if (mLazyDexOpt) {
+            dexOptLRUThresholdInMinutes = 30; // only last 30 minutes of apps for eng builds.
+        } else {
+            dexOptLRUThresholdInMinutes = 7 * 24 * 60; // apps used in the 7 days for users.
+        }
+        mDexOptLRUThresholdInMills = dexOptLRUThresholdInMinutes * 60 * 1000;
+
         String separateProcesses = SystemProperties.get("debug.separate_processes");
         if (separateProcesses != null && separateProcesses.length() > 0) {
             if ("*".equals(separateProcesses)) {
@@ -4568,22 +4580,13 @@
         // The exception is first boot of a non-eng device (aka !mLazyDexOpt), which
         // should do a full dexopt.
         if (mLazyDexOpt || (!isFirstBoot() && mPackageUsage.isHistoricalPackageUsageAvailable())) {
-            // TODO: add a property to control this?
-            long dexOptLRUThresholdInMinutes;
-            if (mLazyDexOpt) {
-                dexOptLRUThresholdInMinutes = 30; // only last 30 minutes of apps for eng builds.
-            } else {
-                dexOptLRUThresholdInMinutes = 7 * 24 * 60; // apps used in the 7 days for users.
-            }
-            long dexOptLRUThresholdInMills = dexOptLRUThresholdInMinutes * 60 * 1000;
-
             int total = pkgs.size();
             int skipped = 0;
             long now = System.currentTimeMillis();
             for (Iterator<PackageParser.Package> i = pkgs.iterator(); i.hasNext();) {
                 PackageParser.Package pkg = i.next();
                 long then = pkg.mLastPackageUsageTimeInMills;
-                if (then + dexOptLRUThresholdInMills < now) {
+                if (then + mDexOptLRUThresholdInMills < now) {
                     if (DEBUG_DEXOPT) {
                         Log.i(TAG, "Skipping dexopt of " + pkg.packageName + " last resumed: " +
                               ((then == 0) ? "never" : new Date(then)));
@@ -13381,4 +13384,25 @@
             return false;
         }
     }
+
+    public void getUsageStatsIfNoPackageUsageInfo() {
+        if (!mPackageUsage.isHistoricalPackageUsageAvailable()) {
+            UsageStatsManager usm = (UsageStatsManager) mContext.getSystemService(Context.USAGE_STATS_SERVICE);
+            if (usm == null) {
+                throw new IllegalStateException("UsageStatsManager must be initialized");
+            }
+            long now = System.currentTimeMillis();
+            Map<String, UsageStats> stats = usm.queryAndAggregateUsageStats(now - mDexOptLRUThresholdInMills, now);
+            for (Map.Entry<String, UsageStats> entry : stats.entrySet()) {
+                String packageName = entry.getKey();
+                PackageParser.Package pkg = mPackages.get(packageName);
+                if (pkg == null) {
+                    continue;
+                }
+                UsageStats usage = entry.getValue();
+                pkg.mLastPackageUsageTimeInMills = usage.getLastTimeUsed();
+                mPackageUsage.mIsHistoricalPackageUsageAvailable = true;
+            }
+        }
+    }
 }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 6009ffd..d7f6130 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -374,6 +374,8 @@
         mSystemServiceManager.startService(UsageStatsService.class);
         mActivityManagerService.setUsageStatsManager(
                 LocalServices.getService(UsageStatsManagerInternal.class));
+        // Update after UsageStatsService is available, needed before performBootDexOpt.
+        mPackageManagerService.getUsageStatsIfNoPackageUsageInfo();
 
         // Tracks whether the updatable WebView is in a ready state and watches for update installs.
         mSystemServiceManager.startService(WebViewUpdateService.class);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 2ed9745..7ff246a 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -38,6 +38,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
@@ -349,8 +350,12 @@
     private class BinderService extends IUsageStatsManager.Stub {
 
         private boolean hasPermission(String callingPackage) {
+            final int callingUid = Binder.getCallingUid();
+            if (callingUid == Process.SYSTEM_UID) {
+                return true;
+            }
             final int mode = mAppOps.checkOp(AppOpsManager.OP_GET_USAGE_STATS,
-                    Binder.getCallingUid(), callingPackage);
+                    callingUid, callingPackage);
             if (mode == AppOpsManager.MODE_DEFAULT) {
                 // The default behavior here is to check if PackageManager has given the app
                 // permission.