Inline Updates - Add the real flow.

This adds real support for inline updates from Play.  This hooks up the
AppUpdateManager Play class to Chrome's InlineUpdateController and
UpdateStateProvider.

play core library that are required to support inline updates.

Binary-Size: This cl brings in the minimal set of new classes from the
Bug: 922714
Change-Id: I1ba932a97502472af956471d8d58c87645a966f7
Reviewed-on: https://chromium-review.googlesource.com/c/1468801
Commit-Queue: Tommy Nyquist <nyquist@chromium.org>
Commit-Queue: David Trainor <dtrainor@chromium.org>
Reviewed-by: Tommy Nyquist <nyquist@chromium.org>
Cr-Commit-Position: refs/heads/master@{#632780}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/inline/FakeAppUpdateManagerWrapper.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/inline/FakeAppUpdateManagerWrapper.java
index 1ad91ea..02d6953f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omaha/inline/FakeAppUpdateManagerWrapper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/inline/FakeAppUpdateManagerWrapper.java
@@ -74,6 +74,7 @@
     @Override
     public boolean startUpdateFlowForResult(AppUpdateInfo appUpdateInfo,
             @AppUpdateType int appUpdateType, Activity activity, int requestCode) {
+        // TODO(dtrainor): Simulate exceptions being thrown or returning false from the super call.
         boolean success =
                 super.startUpdateFlowForResult(appUpdateInfo, appUpdateType, activity, requestCode);
         if (!success) return false;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/inline/InlineAppUpdateManagerFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/inline/InlineAppUpdateManagerFactory.java
index e1630ba..387e4e7e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omaha/inline/InlineAppUpdateManagerFactory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/inline/InlineAppUpdateManagerFactory.java
@@ -12,7 +12,7 @@
 
 /** A factory that creates an {@link AppUpdateManager} instance. */
 public class InlineAppUpdateManagerFactory {
-    private static final boolean sTest = false;
+    private static final boolean sTest = true;
 
     /** @return A new {@link AppUpdateManager} to use to interact with Play for inline updates. */
     public static AppUpdateManager create() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/inline/InlineUpdateController.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/inline/InlineUpdateController.java
index 874e000..1f0c12f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omaha/inline/InlineUpdateController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/inline/InlineUpdateController.java
@@ -5,10 +5,19 @@
 package org.chromium.chrome.browser.omaha.inline;
 
 import android.app.Activity;
+import android.content.IntentSender.SendIntentException;
 import android.os.Handler;
 import android.os.Looper;
 import android.support.annotation.Nullable;
 
+import com.google.android.play.core.appupdate.AppUpdateInfo;
+import com.google.android.play.core.appupdate.AppUpdateManager;
+import com.google.android.play.core.install.InstallState;
+import com.google.android.play.core.install.InstallStateUpdatedListener;
+import com.google.android.play.core.install.model.AppUpdateType;
+import com.google.android.play.core.install.model.InstallStatus;
+import com.google.android.play.core.install.model.UpdateAvailability;
+
 import org.chromium.chrome.browser.omaha.UpdateStatusProvider.UpdateState;
 
 /**
@@ -16,20 +25,29 @@
  * involves hooking up to Play as a listener for install state changes, should only happen if we are
  * in the foreground.
  */
-public class InlineUpdateController {
+public class InlineUpdateController implements InstallStateUpdatedListener {
+    private static final int RESULT_IN_APP_UPDATE_FAILED = 1;
+    private static final int REQUEST_CODE = 8123;
+
     private final Handler mHandler = new Handler(Looper.getMainLooper());
 
     private final Runnable mCallback;
+    private final AppUpdateManager mAppUpdateManager;
 
     private boolean mEnabled;
     private @Nullable @UpdateState Integer mUpdateState;
 
+    private AppUpdateInfo mAppUpdateInfo;
+    private @Nullable @UpdateAvailability Integer mUpdateAvailability;
+    private @Nullable @InstallStatus Integer mInstallStatus;
+
     /**
      * Builds an instance of {@link InlineUpdateController}.
      * @param callback The {@link Runnable} to notify when an inline update state change occurs.
      */
     public InlineUpdateController(Runnable callback) {
         mCallback = callback;
+        mAppUpdateManager = InlineAppUpdateManagerFactory.create();
 
         setEnabled(true);
     }
@@ -40,7 +58,10 @@
 
         if (mEnabled) {
             mUpdateState = UpdateState.NONE;
-            mHandler.post(mCallback);
+            mAppUpdateManager.registerListener(this);
+            pullCurrentState();
+        } else {
+            mAppUpdateManager.unregisterListener(this);
         }
     }
 
@@ -57,10 +78,91 @@
      * cause Chrome to move to the background.
      * @param activity The {@link Activity} to use to interact with Play.
      */
-    public void startUpdate(Activity activity) {}
+    public void startUpdate(Activity activity) {
+        try {
+            boolean success = mAppUpdateManager.startUpdateFlowForResult(
+                    mAppUpdateInfo, AppUpdateType.FLEXIBLE, activity, REQUEST_CODE);
+        } catch (SendIntentException exception) {
+            mInstallStatus = InstallStatus.FAILED;
+        }
+        // TODO(dtrainor): Use success.
+    }
 
     /**
      * Completes the Play installation process, if possible.  This may cause Chrome to restart.
      */
-    public void completeUpdate() {}
+    public void completeUpdate() {
+        mAppUpdateManager.completeUpdate()
+                .addOnSuccessListener(unused -> { pushStatus(); })
+                .addOnFailureListener(exception -> {
+                    mInstallStatus = InstallStatus.FAILED;
+                    pushStatus();
+                });
+    }
+
+    // InstallStateUpdatedListener implementation.
+    @Override
+    public void onStateUpdate(InstallState state) {
+        mInstallStatus = state.installStatus();
+        pushStatus();
+    }
+
+    private void pullCurrentState() {
+        mAppUpdateManager.getAppUpdateInfo()
+                .addOnSuccessListener(info -> {
+                    mAppUpdateInfo = info;
+                    mUpdateAvailability = info.updateAvailability();
+                    mInstallStatus = info.installStatus();
+                    pushStatus();
+                })
+                .addOnFailureListener(exception -> {
+                    mAppUpdateInfo = null;
+                    mUpdateAvailability = UpdateAvailability.UNKNOWN;
+                    mInstallStatus = InstallStatus.UNKNOWN;
+                    pushStatus();
+                });
+    }
+
+    private void pushStatus() {
+        if (!mEnabled || mUpdateAvailability == null || mInstallStatus == null) return;
+
+        @UpdateState
+        int newState = toUpdateState(mUpdateAvailability, mInstallStatus);
+        if (mUpdateState != null && mUpdateState == newState) return;
+
+        mUpdateState = newState;
+        mCallback.run();
+    }
+
+    private static @UpdateState int toUpdateState(
+            @UpdateAvailability int updateAvailability, @InstallStatus int installStatus) {
+        @UpdateState
+        int newStatus = UpdateState.NONE;
+
+        // Note, use InstallStatus first then UpdateAvailability if InstallStatus doesn't indicate
+        // a currently active install.
+        switch (installStatus) {
+            case InstallStatus.PENDING:
+                // Intentional fall through.
+            case InstallStatus.DOWNLOADING:
+                newStatus = UpdateState.INLINE_UPDATE_DOWNLOADING;
+                break;
+            case InstallStatus.DOWNLOADED:
+                newStatus = UpdateState.INLINE_UPDATE_READY;
+                break;
+            case InstallStatus.FAILED:
+                newStatus = UpdateState.INLINE_UPDATE_FAILED;
+                break;
+        }
+
+        if (newStatus == UpdateState.NONE) {
+            switch (updateAvailability) {
+                case UpdateAvailability.UPDATE_AVAILABLE:
+                    newStatus = UpdateState.INLINE_UPDATE_AVAILABLE;
+                    break;
+            }
+        }
+
+        return newStatus;
+    }
 }
\ No newline at end of file