Implement InstanceIDAndroid using InstanceIDWithSubtype.java

Replaces the previous stub implementation with a fully functional
implementation backed by InstanceIDWithSubtype.java.

Part of a series of patches:
1. https://codereview.chromium.org/1832833002 adds InstanceIDWithSubtype
2. this patch
3. https://codereview.chromium.org/1829023002 adds fake and test
4. https://codereview.chromium.org/1854093002 enables InstanceID by default
5. https://codereview.chromium.org/1851423003 switches Push to InstanceIDs

BUG=589461

Review URL: https://codereview.chromium.org/1830983002

Cr-Commit-Position: refs/heads/master@{#387646}
diff --git a/components/gcm_driver/instance_id/android/BUILD.gn b/components/gcm_driver/instance_id/android/BUILD.gn
new file mode 100644
index 0000000..6168aff
--- /dev/null
+++ b/components/gcm_driver/instance_id/android/BUILD.gn
@@ -0,0 +1,26 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/android/rules.gni")
+
+# GYP version: components/gcm_driver.gypi:instance_id_driver_jni_headers
+generate_jni("jni_headers") {
+  sources = [
+    "java/src/org/chromium/components/gcm_driver/instance_id/InstanceIDBridge.java",
+  ]
+  jni_package = "components/gcm_driver/instance_id"
+}
+
+# GYP version: components/gcm_driver.gypi:instance_id_driver_java
+android_library("instance_id_driver_java") {
+  deps = [
+    "//base:base_java",
+    google_play_services_library,
+  ]
+
+  java_files = [
+    "java/src/org/chromium/components/gcm_driver/instance_id/InstanceIDBridge.java",
+    "java/src/org/chromium/components/gcm_driver/instance_id/InstanceIDWithSubtype.java",
+  ]
+}
diff --git a/components/gcm_driver/instance_id/android/component_jni_registrar.cc b/components/gcm_driver/instance_id/android/component_jni_registrar.cc
new file mode 100644
index 0000000..de6705b
--- /dev/null
+++ b/components/gcm_driver/instance_id/android/component_jni_registrar.cc
@@ -0,0 +1,26 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/gcm_driver/instance_id/android/component_jni_registrar.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_registrar.h"
+#include "base/macros.h"
+#include "components/gcm_driver/instance_id/instance_id_android.h"
+
+namespace instance_id {
+namespace android {
+
+static base::android::RegistrationMethod kInstanceIDRegisteredMethods[] = {
+    {"InstanceID", instance_id::InstanceIDAndroid::RegisterJni},
+};
+
+bool RegisterInstanceIDJni(JNIEnv* env) {
+  return base::android::RegisterNativeMethods(
+      env, kInstanceIDRegisteredMethods,
+      arraysize(kInstanceIDRegisteredMethods));
+}
+
+}  // namespace android
+}  // namespace instance_id
diff --git a/components/gcm_driver/instance_id/android/component_jni_registrar.h b/components/gcm_driver/instance_id/android/component_jni_registrar.h
new file mode 100644
index 0000000..dc40f9bc
--- /dev/null
+++ b/components/gcm_driver/instance_id/android/component_jni_registrar.h
@@ -0,0 +1,19 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_GCM_DRIVER_INSTANCE_ID_ANDROID_COMPONENT_JNI_REGISTRAR_H_
+#define COMPONENTS_GCM_DRIVER_INSTANCE_ID_ANDROID_COMPONENT_JNI_REGISTRAR_H_
+
+#include <jni.h>
+
+namespace instance_id {
+namespace android {
+
+// Register all JNI bindings necessary for the gcm_driver/instance_id component.
+bool RegisterInstanceIDJni(JNIEnv* env);
+
+}  // namespace android
+}  // namespace instance_id
+
+#endif  // COMPONENTS_GCM_DRIVER_INSTANCE_ID_ANDROID_COMPONENT_JNI_REGISTRAR_H_
diff --git a/components/gcm_driver/instance_id/android/java/src/org/chromium/components/gcm_driver/instance_id/InstanceIDBridge.java b/components/gcm_driver/instance_id/android/java/src/org/chromium/components/gcm_driver/instance_id/InstanceIDBridge.java
new file mode 100644
index 0000000..a984a24
--- /dev/null
+++ b/components/gcm_driver/instance_id/android/java/src/org/chromium/components/gcm_driver/instance_id/InstanceIDBridge.java
@@ -0,0 +1,146 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.components.gcm_driver.instance_id;
+
+import android.content.Context;
+import android.os.AsyncTask;
+import android.os.Bundle;
+
+import com.google.android.gms.iid.InstanceID;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+
+import java.io.IOException;
+
+/**
+ * Wraps InstanceID and InstanceIDWithSubtype so they can be used over JNI.
+ * Performs disk/network operations on a background thread and replies asynchronously.
+ */
+@JNINamespace("instance_id")
+public class InstanceIDBridge {
+    /** Underlying InstanceID. May be shared by multiple InstanceIDBridges. */
+    private final InstanceID mInstanceID;
+    private long mNativeInstanceIDAndroid;
+
+    private InstanceIDBridge(
+            long nativeInstanceIDAndroid, Context context, String subtype) {
+        mInstanceID = InstanceIDWithSubtype.getInstance(context, subtype);
+        mNativeInstanceIDAndroid = nativeInstanceIDAndroid;
+    }
+
+    /**
+     * Returns a wrapped {@link InstanceIDWithSubtype}. Multiple InstanceIDBridge instances may
+     * share an underlying InstanceIDWithSubtype.
+     */
+    @CalledByNative
+    public static InstanceIDBridge create(
+            long nativeInstanceIDAndroid, Context context, String subtype) {
+        // TODO(johnme): This should also be async.
+        return new InstanceIDBridge(nativeInstanceIDAndroid, context, subtype);
+    }
+
+    /**
+     * Called when our C++ counterpart is destroyed. Clears the handle to our native C++ object,
+     * ensuring it's not called by pending async tasks.
+     */
+    @CalledByNative
+    private void destroy() {
+        mNativeInstanceIDAndroid = 0;
+    }
+
+    /** Wrapper for {@link InstanceID#getId}. */
+    @CalledByNative
+    public String getId() {
+        // TODO(johnme): This should also be async.
+        return mInstanceID.getId();
+    }
+
+    /** Wrapper for {@link InstanceID#getCreationTime}. */
+    @CalledByNative
+    public long getCreationTime() {
+        // TODO(johnme): This should also be async.
+        return mInstanceID.getCreationTime();
+    }
+
+    /** Async wrapper for {@link InstanceID#getToken(String, String, Bundle)}. */
+    @CalledByNative
+    private void getToken(final int requestId, final String authorizedEntity, final String scope,
+            String[] extrasStrings) {
+        final Bundle extras = new Bundle();
+        assert extrasStrings.length % 2 == 0;
+        for (int i = 0; i < extrasStrings.length; i += 2) {
+            extras.putString(extrasStrings[i], extrasStrings[i + 1]);
+        }
+        new AsyncTask<Void, Void, String>() {
+            @Override
+            protected String doInBackground(Void... params) {
+                try {
+                    return mInstanceID.getToken(authorizedEntity, scope, extras);
+                } catch (IOException ex) {
+                    return "";
+                }
+            }
+            @Override
+            protected void onPostExecute(String token) {
+                if (mNativeInstanceIDAndroid != 0) {
+                    nativeDidGetToken(mNativeInstanceIDAndroid, requestId, token);
+                }
+            }
+        }.execute();
+    }
+
+    /** Async wrapper for {@link InstanceID#deleteToken(String, String)}. */
+    @CalledByNative
+    private void deleteToken(
+            final int requestId, final String authorizedEntity, final String scope) {
+        new AsyncTask<Void, Void, Boolean>() {
+            @Override
+            protected Boolean doInBackground(Void... params) {
+                try {
+                    mInstanceID.deleteToken(authorizedEntity, scope);
+                    return true;
+                } catch (IOException ex) {
+                    return false;
+                }
+            }
+            @Override
+            protected void onPostExecute(Boolean success) {
+                if (mNativeInstanceIDAndroid != 0) {
+                    nativeDidDeleteToken(mNativeInstanceIDAndroid, requestId, success);
+                }
+            }
+        }.execute();
+    }
+
+    /** Async wrapper for {@link InstanceID#deleteInstanceID}. */
+    @CalledByNative
+    private void deleteInstanceID(final int requestId) {
+        new AsyncTask<Void, Void, Boolean>() {
+            @Override
+            protected Boolean doInBackground(Void... params) {
+                try {
+                    mInstanceID.deleteInstanceID();
+                    return true;
+                } catch (IOException ex) {
+                    return false;
+                }
+            }
+            @Override
+            protected void onPostExecute(Boolean success) {
+                if (mNativeInstanceIDAndroid != 0) {
+                    nativeDidDeleteID(mNativeInstanceIDAndroid, requestId, success);
+                }
+            }
+        }.execute();
+    }
+
+    private native void nativeDidGetToken(
+            long nativeInstanceIDAndroid, int requestId, String token);
+    private native void nativeDidDeleteToken(
+            long nativeInstanceIDAndroid, int requestId, boolean success);
+    private native void nativeDidDeleteID(
+            long nativeInstanceIDAndroid, int requestId, boolean success);
+}
\ No newline at end of file