Otadexopt: Expose progress percentage

To be able to report progress of an A/B OTA dexopt, expose a
progress function that the script can query.

Bug: 25612095
Bug: 29223204
Change-Id: Ie8162946d18f6fa78649a40ad5d3949d31a181cd
diff --git a/core/java/android/content/pm/IOtaDexopt.aidl b/core/java/android/content/pm/IOtaDexopt.aidl
index 8f38d6f..786a77f 100644
--- a/core/java/android/content/pm/IOtaDexopt.aidl
+++ b/core/java/android/content/pm/IOtaDexopt.aidl
@@ -42,6 +42,12 @@
     boolean isDone();
 
     /**
+     * Return the progress (0..1) made in this session. When {@link #isDone() isDone} returns
+     * true, the progress value will be 1.
+     */
+    float getProgress();
+
+    /**
      * Optimize the next package. Note: this command is synchronous, that is, only returns after
      * the package has been dexopted (or dexopting failed).
      */
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 649a27c..df91f4a1 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -54,6 +54,7 @@
 
     // TODO: Evaluate the need for WeakReferences here.
     private List<PackageParser.Package> mDexoptPackages;
+    private int completeSize;
 
     public OtaDexoptService(Context context, PackageManagerService packageManagerService) {
         this.mContext = context;
@@ -91,6 +92,7 @@
             mDexoptPackages = PackageManagerServiceUtils.getPackagesForDexopt(
                     mPackageManagerService.mPackages.values(), mPackageManagerService);
         }
+        completeSize = mDexoptPackages.size();
     }
 
     @Override
@@ -111,6 +113,14 @@
     }
 
     @Override
+    public synchronized float getProgress() throws RemoteException {
+        if (completeSize == 0) {
+            return 1f;
+        }
+        return (completeSize - mDexoptPackages.size()) / ((float)completeSize);
+    }
+
+    @Override
     public synchronized void dexoptNextPackage() throws RemoteException {
         if (mDexoptPackages == null) {
             throw new IllegalStateException("dexoptNextPackage() called before prepare()");
diff --git a/services/core/java/com/android/server/pm/OtaDexoptShellCommand.java b/services/core/java/com/android/server/pm/OtaDexoptShellCommand.java
index ea9cf17..e8fdfa5 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptShellCommand.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptShellCommand.java
@@ -46,6 +46,8 @@
                     return runOtaDone();
                 case "step":
                     return runOtaStep();
+                case "progress":
+                    return runOtaProgress();
                 default:
                     return handleDefaultCommands(cmd);
             }
@@ -81,6 +83,13 @@
         return 0;
     }
 
+    private int runOtaProgress() throws RemoteException {
+        final float progress = mInterface.getProgress();
+        final PrintWriter pw = getOutPrintWriter();
+        pw.format("%.2f", progress);
+        return 0;
+    }
+
     @Override
     public void onHelp() {
         final PrintWriter pw = getOutPrintWriter();