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.gypi b/components/gcm_driver.gypi
index 6590f8b..042397f 100644
--- a/components/gcm_driver.gypi
+++ b/components/gcm_driver.gypi
@@ -175,6 +175,8 @@
       ],
       'sources': [
         # Note: file list duplicated in GN build.
+        'gcm_driver/instance_id/android/component_jni_registrar.cc',
+        'gcm_driver/instance_id/android/component_jni_registrar.h',
         'gcm_driver/instance_id/instance_id.cc',
         'gcm_driver/instance_id/instance_id.h',
         'gcm_driver/instance_id/instance_id_android.cc',
@@ -186,6 +188,9 @@
       ],
       'conditions': [
         ['OS == "android"', {
+          'dependencies': [
+            'instance_id_driver_jni_headers',
+          ],
           'sources!': [
             'gcm_driver/instance_id/instance_id_impl.cc',
             'gcm_driver/instance_id/instance_id_impl.h',
@@ -300,8 +305,7 @@
           'target_name': 'gcm_driver_java',
           'type': 'none',
           'dependencies': [
-            '../base/base.gyp:base',
-            # TODO(johnme): Fix the layering violation of depending on content/
+            '../base/base.gyp:base_java',
             '../content/content.gyp:content_java',
             '../sync/sync.gyp:sync_java',
           ],
@@ -322,6 +326,31 @@
           },
           'includes': [ '../build/jni_generator.gypi' ],
         },
+        {
+          # GN version: //components/gcm_driver/instance_id/android:instance_id_driver_java
+          'target_name': 'instance_id_driver_java',
+          'type': 'none',
+          'dependencies': [
+            '../base/base.gyp:base_java',
+            '../third_party/android_tools/android_tools.gyp:google_play_services_javalib',
+          ],
+          'variables': {
+            'java_in_dir': 'gcm_driver/instance_id/android/java',
+          },
+          'includes': [ '../build/java.gypi' ],
+        },
+        {
+          # GN version: //components/gcm_driver/instance_id/android:jni_headers
+          'target_name': 'instance_id_driver_jni_headers',
+          'type': 'none',
+          'sources': [
+            'gcm_driver/instance_id/android/java/src/org/chromium/components/gcm_driver/instance_id/InstanceIDBridge.java',
+          ],
+          'variables': {
+            'jni_gen_package': 'components/gcm_driver/instance_id',
+          },
+          'includes': [ '../build/jni_generator.gypi' ],
+        },
       ],
      },
     ],
diff --git a/components/gcm_driver/android/BUILD.gn b/components/gcm_driver/android/BUILD.gn
index af75f01f..3f674c61 100644
--- a/components/gcm_driver/android/BUILD.gn
+++ b/components/gcm_driver/android/BUILD.gn
@@ -17,9 +17,7 @@
   deps = [
     "//base:base_java",
     "//content/public/android:content_java",
-    "//sync/android:sync_java",
     "//third_party/android_tools:android_gcm_java",
-    "//third_party/cacheinvalidation:cacheinvalidation_javalib",
     "//third_party/jsr-305:jsr_305_javalib",
   ]
 
diff --git a/components/gcm_driver/android/component_jni_registrar.cc b/components/gcm_driver/android/component_jni_registrar.cc
index 0ef0601..025820c 100644
--- a/components/gcm_driver/android/component_jni_registrar.cc
+++ b/components/gcm_driver/android/component_jni_registrar.cc
@@ -14,7 +14,7 @@
 namespace android {
 
 static base::android::RegistrationMethod kGCMDriverRegisteredMethods[] = {
-    {"GCMDriver", gcm::GCMDriverAndroid::RegisterBindings},
+    {"GCMDriver", gcm::GCMDriverAndroid::RegisterJni},
 };
 
 bool RegisterGCMDriverJni(JNIEnv* env) {
diff --git a/components/gcm_driver/gcm_driver_android.cc b/components/gcm_driver/gcm_driver_android.cc
index 21bcfa0..40e204d 100644
--- a/components/gcm_driver/gcm_driver_android.cc
+++ b/components/gcm_driver/gcm_driver_android.cc
@@ -111,7 +111,7 @@
 }
 
 // static
-bool GCMDriverAndroid::RegisterBindings(JNIEnv* env) {
+bool GCMDriverAndroid::RegisterJni(JNIEnv* env) {
   return RegisterNativesImpl(env);
 }
 
diff --git a/components/gcm_driver/gcm_driver_android.h b/components/gcm_driver/gcm_driver_android.h
index 5944b85..ab5c9f3 100644
--- a/components/gcm_driver/gcm_driver_android.h
+++ b/components/gcm_driver/gcm_driver_android.h
@@ -51,7 +51,7 @@
       const base::android::JavaParamRef<jobjectArray>& data_keys_and_values);
 
   // Register JNI methods.
-  static bool RegisterBindings(JNIEnv* env);
+  static bool RegisterJni(JNIEnv* env);
 
   // GCMDriver implementation:
   void OnSignedIn() override;
diff --git a/components/gcm_driver/instance_id/BUILD.gn b/components/gcm_driver/instance_id/BUILD.gn
index 3e77929..ec00c0b 100644
--- a/components/gcm_driver/instance_id/BUILD.gn
+++ b/components/gcm_driver/instance_id/BUILD.gn
@@ -5,6 +5,8 @@
 # GYP version: components/gcm_driver.gypi:instance_id_driver
 source_set("instance_id") {
   sources = [
+    "android/component_jni_registrar.cc",
+    "android/component_jni_registrar.h",
     "instance_id.cc",
     "instance_id.h",
     "instance_id_driver.cc",
@@ -29,6 +31,7 @@
       "instance_id_android.cc",
       "instance_id_android.h",
     ]
+    deps += [ "android:jni_headers" ]
   }
 }
 
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
diff --git a/components/gcm_driver/instance_id/instance_id.cc b/components/gcm_driver/instance_id/instance_id.cc
index d89c997e..d48b5ca 100644
--- a/components/gcm_driver/instance_id/instance_id.cc
+++ b/components/gcm_driver/instance_id/instance_id.cc
@@ -6,14 +6,9 @@
 
 namespace instance_id {
 
-InstanceID::InstanceID(const std::string& app_id,
-                       gcm::InstanceIDHandler* handler)
-    : handler_(handler), app_id_(app_id) {
-  DCHECK(handler_);
-}
+InstanceID::InstanceID(const std::string& app_id) : app_id_(app_id) {}
 
-InstanceID::~InstanceID() {
-}
+InstanceID::~InstanceID() {}
 
 void InstanceID::SetTokenRefreshCallback(const TokenRefreshCallback& callback) {
   token_refresh_callback_ = callback;
diff --git a/components/gcm_driver/instance_id/instance_id.h b/components/gcm_driver/instance_id/instance_id.h
index fb69a11..de9bc21 100644
--- a/components/gcm_driver/instance_id/instance_id.h
+++ b/components/gcm_driver/instance_id/instance_id.h
@@ -57,7 +57,7 @@
   // Creator.
   // |app_id|: identifies the application that uses the Instance ID.
   // |handler|: provides the GCM functionality needed to support Instance ID.
-  //            Must outlive this class.
+  //            Must outlive this class. On Android, this can be null instead.
   static scoped_ptr<InstanceID> Create(const std::string& app_id,
                                        gcm::InstanceIDHandler* handler);
 
@@ -106,17 +106,11 @@
   std::string app_id() const { return app_id_; }
 
  protected:
-  InstanceID(const std::string& app_id, gcm::InstanceIDHandler* handler);
+  InstanceID(const std::string& app_id);
 
   void NotifyTokenRefresh(bool update_id);
 
-  gcm::InstanceIDHandler* handler() const { return handler_; }
-
  private:
-  // Owned by GCMProfileServiceFactory, which is a dependency of
-  // InstanceIDProfileServiceFactory, which owns this.
-  gcm::InstanceIDHandler* handler_;
-
   std::string app_id_;
   TokenRefreshCallback token_refresh_callback_;
 
diff --git a/components/gcm_driver/instance_id/instance_id_android.cc b/components/gcm_driver/instance_id/instance_id_android.cc
index 9611567..f2fbd21 100644
--- a/components/gcm_driver/instance_id/instance_id_android.cc
+++ b/components/gcm_driver/instance_id/instance_id_android.cc
@@ -4,49 +4,175 @@
 
 #include "components/gcm_driver/instance_id/instance_id_android.h"
 
+#include <stdint.h>
+
+#include "base/android/context_utils.h"
+#include "base/android/jni_android.h"
+#include "base/android/jni_array.h"
+#include "base/android/jni_string.h"
+#include "base/bind.h"
+#include "base/location.h"
 #include "base/logging.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "jni/InstanceIDBridge_jni.h"
+
+using base::android::AttachCurrentThread;
+using base::android::ConvertJavaStringToUTF8;
+using base::android::ConvertUTF8ToJavaString;
 
 namespace instance_id {
 
 // static
-scoped_ptr<InstanceID> InstanceID::Create(const std::string& app_id,
-                                          gcm::InstanceIDHandler* handler) {
-  return make_scoped_ptr(new InstanceIDAndroid(app_id, handler));
+bool InstanceIDAndroid::RegisterJni(JNIEnv* env) {
+  return RegisterNativesImpl(env);
 }
 
-InstanceIDAndroid::InstanceIDAndroid(const std::string& app_id,
-                                     gcm::InstanceIDHandler* handler)
-    : InstanceID(app_id, handler) {}
+// static
+scoped_ptr<InstanceID> InstanceID::Create(const std::string& app_id,
+                                          gcm::InstanceIDHandler* unused) {
+  return make_scoped_ptr(new InstanceIDAndroid(app_id));
+}
+
+InstanceIDAndroid::InstanceIDAndroid(const std::string& app_id)
+    : InstanceID(app_id) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  DCHECK(!app_id.empty()) << "Empty app_id is not supported";
+  // The |app_id| is stored in GCM's category field by the desktop InstanceID
+  // implementation, but because the category is reserved for the app's package
+  // name on Android the subtype field is used instead.
+  std::string subtype = app_id;
+
+  JNIEnv* env = AttachCurrentThread();
+  java_ref_.Reset(Java_InstanceIDBridge_create(
+      env, reinterpret_cast<intptr_t>(this),
+      base::android::GetApplicationContext(),
+      ConvertUTF8ToJavaString(env, subtype).obj()));
+}
 
 InstanceIDAndroid::~InstanceIDAndroid() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  JNIEnv* env = AttachCurrentThread();
+  Java_InstanceIDBridge_destroy(env, java_ref_.obj());
 }
 
 void InstanceIDAndroid::GetID(const GetIDCallback& callback) {
-  NOTIMPLEMENTED();
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  JNIEnv* env = AttachCurrentThread();
+  std::string id = ConvertJavaStringToUTF8(
+      Java_InstanceIDBridge_getId(env, java_ref_.obj()));
+  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                                base::Bind(callback, id));
 }
 
 void InstanceIDAndroid::GetCreationTime(
     const GetCreationTimeCallback& callback) {
-  NOTIMPLEMENTED();
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  JNIEnv* env = AttachCurrentThread();
+  int64_t creation_time_unix_ms =
+      Java_InstanceIDBridge_getCreationTime(env, java_ref_.obj());
+  base::Time creation_time;
+  // If the InstanceID's getId, getToken and deleteToken methods have never been
+  // called, or deleteInstanceID has cleared it since, creation time will be 0.
+  if (creation_time_unix_ms) {
+    creation_time = base::Time::UnixEpoch() +
+                    base::TimeDelta::FromMilliseconds(creation_time_unix_ms);
+  }
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::Bind(callback, creation_time));
 }
 
 void InstanceIDAndroid::GetToken(
-    const std::string& audience,
+    const std::string& authorized_entity,
     const std::string& scope,
     const std::map<std::string, std::string>& options,
     const GetTokenCallback& callback) {
-  NOTIMPLEMENTED();
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  int32_t request_id = get_token_callbacks_.Add(new GetTokenCallback(callback));
+
+  std::vector<std::string> options_strings;
+  for (const auto& entry : options) {
+    options_strings.push_back(entry.first);
+    options_strings.push_back(entry.second);
+  }
+
+  JNIEnv* env = AttachCurrentThread();
+  Java_InstanceIDBridge_getToken(
+      env, java_ref_.obj(), request_id,
+      ConvertUTF8ToJavaString(env, authorized_entity).obj(),
+      ConvertUTF8ToJavaString(env, scope).obj(),
+      base::android::ToJavaArrayOfStrings(env, options_strings).obj());
 }
 
-void InstanceIDAndroid::DeleteToken(const std::string& audience,
+void InstanceIDAndroid::DeleteToken(const std::string& authorized_entity,
                                     const std::string& scope,
                                     const DeleteTokenCallback& callback) {
-  NOTIMPLEMENTED();
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  int32_t request_id =
+      delete_token_callbacks_.Add(new DeleteTokenCallback(callback));
+
+  JNIEnv* env = AttachCurrentThread();
+  Java_InstanceIDBridge_deleteToken(
+      env, java_ref_.obj(), request_id,
+      ConvertUTF8ToJavaString(env, authorized_entity).obj(),
+      ConvertUTF8ToJavaString(env, scope).obj());
 }
 
 void InstanceIDAndroid::DeleteID(const DeleteIDCallback& callback) {
-  NOTIMPLEMENTED();
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  int32_t request_id = delete_id_callbacks_.Add(new DeleteIDCallback(callback));
+
+  JNIEnv* env = AttachCurrentThread();
+  Java_InstanceIDBridge_deleteInstanceID(env, java_ref_.obj(), request_id);
+}
+
+void InstanceIDAndroid::DidGetToken(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& obj,
+    jint request_id,
+    const base::android::JavaParamRef<jstring>& jtoken) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  GetTokenCallback* callback = get_token_callbacks_.Lookup(request_id);
+  DCHECK(callback);
+  std::string token = ConvertJavaStringToUTF8(jtoken);
+  callback->Run(
+      token, token.empty() ? InstanceID::UNKNOWN_ERROR : InstanceID::SUCCESS);
+  get_token_callbacks_.Remove(request_id);
+}
+
+void InstanceIDAndroid::DidDeleteToken(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& obj,
+    jint request_id,
+    jboolean success) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  DeleteTokenCallback* callback = delete_token_callbacks_.Lookup(request_id);
+  DCHECK(callback);
+  callback->Run(success ? InstanceID::SUCCESS : InstanceID::UNKNOWN_ERROR);
+  delete_token_callbacks_.Remove(request_id);
+}
+
+void InstanceIDAndroid::DidDeleteID(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& obj,
+    jint request_id,
+    jboolean success) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  DeleteIDCallback* callback = delete_id_callbacks_.Lookup(request_id);
+  DCHECK(callback);
+  callback->Run(success ? InstanceID::SUCCESS : InstanceID::UNKNOWN_ERROR);
+  delete_id_callbacks_.Remove(request_id);
 }
 
 }  // namespace instance_id
diff --git a/components/gcm_driver/instance_id/instance_id_android.h b/components/gcm_driver/instance_id/instance_id_android.h
index de0ff62d..a1152960 100644
--- a/components/gcm_driver/instance_id/instance_id_android.h
+++ b/components/gcm_driver/instance_id/instance_id_android.h
@@ -5,12 +5,17 @@
 #ifndef COMPONENTS_GCM_DRIVER_INSTANCE_ID_INSTANCE_ID_ANDROID_H_
 #define COMPONENTS_GCM_DRIVER_INSTANCE_ID_INSTANCE_ID_ANDROID_H_
 
+#include <jni.h>
+
 #include <map>
 #include <string>
 
+#include "base/android/scoped_java_ref.h"
 #include "base/callback.h"
 #include "base/compiler_specific.h"
+#include "base/id_map.h"
 #include "base/macros.h"
+#include "base/threading/thread_checker.h"
 #include "base/time/time.h"
 #include "components/gcm_driver/instance_id/instance_id.h"
 
@@ -19,10 +24,13 @@
 // InstanceID implementation for Android.
 class InstanceIDAndroid : public InstanceID {
  public:
-  InstanceIDAndroid(const std::string& app_id, gcm::InstanceIDHandler* handler);
+  // Register JNI methods.
+  static bool RegisterJni(JNIEnv* env);
+
+  InstanceIDAndroid(const std::string& app_id);
   ~InstanceIDAndroid() override;
 
-  // InstanceID:
+  // InstanceID implementation:
   void GetID(const GetIDCallback& callback) override;
   void GetCreationTime(const GetCreationTimeCallback& callback) override;
   void GetToken(const std::string& audience,
@@ -34,7 +42,29 @@
                    const DeleteTokenCallback& callback) override;
   void DeleteID(const DeleteIDCallback& callback) override;
 
+  // Methods called from Java via JNI:
+  void DidGetToken(JNIEnv* env,
+                   const base::android::JavaParamRef<jobject>& obj,
+                   jint request_id,
+                   const base::android::JavaParamRef<jstring>& jtoken);
+  void DidDeleteToken(JNIEnv* env,
+                      const base::android::JavaParamRef<jobject>& obj,
+                      jint request_id,
+                      jboolean success);
+  void DidDeleteID(JNIEnv* env,
+                   const base::android::JavaParamRef<jobject>& obj,
+                   jint request_id,
+                   jboolean success);
+
  private:
+  base::android::ScopedJavaGlobalRef<jobject> java_ref_;
+
+  IDMap<GetTokenCallback, IDMapOwnPointer> get_token_callbacks_;
+  IDMap<DeleteTokenCallback, IDMapOwnPointer> delete_token_callbacks_;
+  IDMap<DeleteIDCallback, IDMapOwnPointer> delete_id_callbacks_;
+
+  base::ThreadChecker thread_checker_;
+
   DISALLOW_COPY_AND_ASSIGN(InstanceIDAndroid);
 };
 
diff --git a/components/gcm_driver/instance_id/instance_id_driver.cc b/components/gcm_driver/instance_id/instance_id_driver.cc
index 374be22..937d77b 100644
--- a/components/gcm_driver/instance_id/instance_id_driver.cc
+++ b/components/gcm_driver/instance_id/instance_id_driver.cc
@@ -12,22 +12,15 @@
 namespace instance_id {
 
 namespace {
-#if !defined(OS_ANDROID)
 const char kInstanceIDFieldTrialName[] = "InstanceID";
 const char kInstanceIDFieldTrialEnabledGroupName[] = "Enabled";
-#endif    // !defined(OS_ANDROID)
 }  // namespace
 
 // static
 bool InstanceIDDriver::IsInstanceIDEnabled() {
-#if defined(OS_ANDROID)
-  // Not implemented yet.
-  return false;
-#else
   std::string group_name =
       base::FieldTrialList::FindFullName(kInstanceIDFieldTrialName);
   return group_name == kInstanceIDFieldTrialEnabledGroupName;
-#endif    // defined(OS_ANDROID)
 }
 
 InstanceIDDriver::InstanceIDDriver(gcm::GCMDriver* gcm_driver)
diff --git a/components/gcm_driver/instance_id/instance_id_impl.cc b/components/gcm_driver/instance_id/instance_id_impl.cc
index ee1ea35..f7e5014 100644
--- a/components/gcm_driver/instance_id/instance_id_impl.cc
+++ b/components/gcm_driver/instance_id/instance_id_impl.cc
@@ -54,8 +54,9 @@
 
 InstanceIDImpl::InstanceIDImpl(const std::string& app_id,
                                gcm::InstanceIDHandler* handler)
-    : InstanceID(app_id, handler), weak_ptr_factory_(this) {
-  handler->GetInstanceIDData(
+    : InstanceID(app_id), handler_(handler), weak_ptr_factory_(this) {
+  DCHECK(handler_);
+  handler_->GetInstanceIDData(
       app_id, base::Bind(&InstanceIDImpl::GetInstanceIDDataCompleted,
                          weak_ptr_factory_.GetWeakPtr()));
 }
@@ -126,9 +127,9 @@
     const GetTokenCallback& callback) {
   EnsureIDGenerated();
 
-  handler()->GetToken(app_id(), authorized_entity, scope, options,
-                      base::Bind(&InstanceIDImpl::OnGetTokenCompleted,
-                                 weak_ptr_factory_.GetWeakPtr(), callback));
+  handler_->GetToken(app_id(), authorized_entity, scope, options,
+                     base::Bind(&InstanceIDImpl::OnGetTokenCompleted,
+                                weak_ptr_factory_.GetWeakPtr(), callback));
 }
 
 void InstanceIDImpl::DeleteToken(const std::string& authorized_entity,
@@ -160,9 +161,9 @@
     return;
   }
 
-  handler()->DeleteToken(app_id(), authorized_entity, scope,
-                         base::Bind(&InstanceIDImpl::OnDeleteTokenCompleted,
-                                    weak_ptr_factory_.GetWeakPtr(), callback));
+  handler_->DeleteToken(app_id(), authorized_entity, scope,
+                        base::Bind(&InstanceIDImpl::OnDeleteTokenCompleted,
+                                   weak_ptr_factory_.GetWeakPtr(), callback));
 }
 
 void InstanceIDImpl::DeleteID(const DeleteIDCallback& callback) {
@@ -184,11 +185,11 @@
     return;
   }
 
-  handler()->DeleteAllTokensForApp(
+  handler_->DeleteAllTokensForApp(
       app_id(), base::Bind(&InstanceIDImpl::OnDeleteIDCompleted,
                            weak_ptr_factory_.GetWeakPtr(), callback));
 
-  handler()->RemoveInstanceIDData(app_id());
+  handler_->RemoveInstanceIDData(app_id());
 
   id_.clear();
   creation_time_ = base::Time();
@@ -261,7 +262,7 @@
   creation_time_ = base::Time::Now();
 
   // Save to the persistent store.
-  handler()->AddInstanceIDData(
+  handler_->AddInstanceIDData(
       app_id(), id_, base::Int64ToString(creation_time_.ToInternalValue()));
 }
 
diff --git a/components/gcm_driver/instance_id/instance_id_impl.h b/components/gcm_driver/instance_id/instance_id_impl.h
index dd4569a..d36264c3 100644
--- a/components/gcm_driver/instance_id/instance_id_impl.h
+++ b/components/gcm_driver/instance_id/instance_id_impl.h
@@ -66,6 +66,10 @@
                      const DeleteTokenCallback& callback);
   void DoDeleteID(const DeleteIDCallback& callback);
 
+  // Owned by GCMProfileServiceFactory, which is a dependency of
+  // InstanceIDProfileServiceFactory, which owns this.
+  gcm::InstanceIDHandler* handler_;
+
   gcm::GCMDelayedTaskController delayed_task_controller_;
 
   // The generated Instance ID.