chromium/components/gcm_driver/gcm_driver_android.cc

// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/gcm_driver/gcm_driver_android.h"

#include <stddef.h>
#include <stdint.h>

#include "base/android/jni_android.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/compiler_specific.h"
#include "base/logging.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner.h"

// Must come after all headers that specialize FromJniType() / ToJniType().
#include "components/gcm_driver/android/jni_headers/GCMDriver_jni.h"

using base::android::AppendJavaStringArrayToStringVector;
using base::android::AttachCurrentThread;
using base::android::ConvertJavaStringToUTF8;
using base::android::ConvertUTF8ToJavaString;
using base::android::JavaByteArrayToString;
using base::android::JavaParamRef;

namespace gcm {

GCMDriverAndroid::GCMDriverAndroid(
    const base::FilePath& store_path,
    const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner)
    : GCMDriver(store_path, blocking_task_runner), recorder_(this) {
  JNIEnv* env = AttachCurrentThread();
  java_ref_.Reset(Java_GCMDriver_create(env, reinterpret_cast<intptr_t>(this)));
}

GCMDriverAndroid::~GCMDriverAndroid() {
  JNIEnv* env = AttachCurrentThread();
  Java_GCMDriver_destroy(env, java_ref_);
}

void GCMDriverAndroid::OnRegisterFinished(
    JNIEnv* env,
    const JavaParamRef<jobject>& obj,
    const JavaParamRef<jstring>& j_app_id,
    const JavaParamRef<jstring>& j_registration_id,
    jboolean success) {
  std::string app_id = ConvertJavaStringToUTF8(env, j_app_id);
  std::string registration_id = ConvertJavaStringToUTF8(env, j_registration_id);
  GCMClient::Result result =
      success ? GCMClient::SUCCESS : GCMClient::UNKNOWN_ERROR;

  recorder_.RecordRegistrationResponse(app_id, success);

  RegisterFinished(app_id, registration_id, result);
}

void GCMDriverAndroid::OnUnregisterFinished(
    JNIEnv* env,
    const JavaParamRef<jobject>& obj,
    const JavaParamRef<jstring>& j_app_id,
    jboolean success) {
  std::string app_id = ConvertJavaStringToUTF8(env, j_app_id);
  GCMClient::Result result =
      success ? GCMClient::SUCCESS : GCMClient::UNKNOWN_ERROR;

  recorder_.RecordUnregistrationResponse(app_id, success);

  RemoveEncryptionInfoAfterUnregister(app_id, result);
}

void GCMDriverAndroid::OnMessageReceived(
    JNIEnv* env,
    const JavaParamRef<jobject>& obj,
    const JavaParamRef<jstring>& j_app_id,
    const JavaParamRef<jstring>& j_sender_id,
    const JavaParamRef<jstring>& j_message_id,
    const JavaParamRef<jstring>& j_collapse_key,
    const JavaParamRef<jbyteArray>& j_raw_data,
    const JavaParamRef<jobjectArray>& j_data_keys_and_values) {
  std::string app_id = ConvertJavaStringToUTF8(env, j_app_id);

  int message_byte_size = 0;

  IncomingMessage message;
  message.sender_id = ConvertJavaStringToUTF8(env, j_sender_id);

  if (!j_message_id.is_null())
    ConvertJavaStringToUTF8(env, j_message_id, &message.message_id);
  if (!j_collapse_key.is_null())
    ConvertJavaStringToUTF8(env, j_collapse_key, &message.collapse_key);

  // Expand j_data_keys_and_values from array to map.
  std::vector<std::string> data_keys_and_values;
  AppendJavaStringArrayToStringVector(env, j_data_keys_and_values,
                                      &data_keys_and_values);
  for (size_t i = 0; i + 1 < data_keys_and_values.size(); i += 2) {
    message.data[data_keys_and_values[i]] = data_keys_and_values[i + 1];
    message_byte_size += data_keys_and_values[i + 1].size();
  }
  // Convert j_raw_data from byte[] to binary std::string.
  if (j_raw_data) {
    JavaByteArrayToString(env, j_raw_data, &message.raw_data);

    message_byte_size += message.raw_data.size();
  }

  recorder_.RecordDataMessageReceived(app_id, message.sender_id,
                                      message_byte_size);

  DispatchMessage(app_id, message);
}

void GCMDriverAndroid::ValidateRegistration(
    const std::string& app_id,
    const std::vector<std::string>& sender_ids,
    const std::string& registration_id,
    ValidateRegistrationCallback callback) {
  // gcm_driver doesn't store registration IDs on Android, so assume it's valid.
  base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
      FROM_HERE, base::BindOnce(std::move(callback), true /* is_valid */));
}

void GCMDriverAndroid::AddAppHandler(const std::string& app_id,
                                     GCMAppHandler* handler) {
  GCMDriver::AddAppHandler(app_id, handler);
  JNIEnv* env = AttachCurrentThread();
  // TODO(melandory, mamir): check if messages were persisted
  // and only then go to java.
  Java_GCMDriver_replayPersistedMessages(env, java_ref_,
                                         ConvertUTF8ToJavaString(env, app_id));
}

void GCMDriverAndroid::AddConnectionObserver(GCMConnectionObserver* observer) {}

void GCMDriverAndroid::RemoveConnectionObserver(
    GCMConnectionObserver* observer) {}

GCMClient* GCMDriverAndroid::GetGCMClientForTesting() const {
  NOTIMPLEMENTED();
  return NULL;
}

bool GCMDriverAndroid::IsStarted() const {
  return true;
}

bool GCMDriverAndroid::IsConnected() const {
  // TODO(gcm): hook up to GCM connected status
  return true;
}

void GCMDriverAndroid::GetGCMStatistics(GetGCMStatisticsCallback callback,
                                        ClearActivityLogs clear_logs) {
  DCHECK(!callback.is_null());

  if (clear_logs == CLEAR_LOGS)
    recorder_.Clear();

  GCMClient::GCMStatistics stats;
  stats.is_recording = recorder_.is_recording();

  recorder_.CollectActivities(&stats.recorded_activities);

  std::move(callback).Run(stats);
}

void GCMDriverAndroid::SetGCMRecording(
    const GCMStatisticsRecordingCallback& callback,
    bool recording) {
  DCHECK(!callback.is_null());

  gcm_statistics_recording_callback_ = callback;
  recorder_.set_is_recording(recording);

  GCMClient::GCMStatistics stats;
  stats.is_recording = recording;

  recorder_.CollectActivities(&stats.recorded_activities);

  callback.Run(stats);
}

void GCMDriverAndroid::SetAccountTokens(
    const std::vector<GCMClient::AccountTokenInfo>& account_tokens) {
  NOTIMPLEMENTED();
}

void GCMDriverAndroid::UpdateAccountMapping(
    const AccountMapping& account_mapping) {
  NOTIMPLEMENTED();
}

void GCMDriverAndroid::RemoveAccountMapping(const CoreAccountId& account_id) {
  NOTIMPLEMENTED();
}

base::Time GCMDriverAndroid::GetLastTokenFetchTime() {
  NOTIMPLEMENTED();
  return base::Time();
}

void GCMDriverAndroid::SetLastTokenFetchTime(const base::Time& time) {
  NOTIMPLEMENTED();
}

InstanceIDHandler* GCMDriverAndroid::GetInstanceIDHandlerInternal() {
  // Not supported for Android.
  return NULL;
}

void GCMDriverAndroid::AddHeartbeatInterval(const std::string& scope,
                                            int interval_ms) {}

void GCMDriverAndroid::RemoveHeartbeatInterval(const std::string& scope) {}

void GCMDriverAndroid::OnActivityRecorded() {
  DCHECK(gcm_statistics_recording_callback_);

  GCMClient::GCMStatistics stats;
  stats.is_recording = recorder_.is_recording();

  recorder_.CollectActivities(&stats.recorded_activities);

  gcm_statistics_recording_callback_.Run(stats);
}

GCMClient::Result GCMDriverAndroid::EnsureStarted(
    GCMClient::StartMode start_mode) {
  // TODO(johnme): Maybe we should check if GMS is available?
  return GCMClient::SUCCESS;
}

void GCMDriverAndroid::RegisterImpl(
    const std::string& app_id,
    const std::vector<std::string>& sender_ids) {
  DCHECK_EQ(1u, sender_ids.size());
  JNIEnv* env = AttachCurrentThread();

  recorder_.RecordRegistrationSent(app_id);

  Java_GCMDriver_register(env, java_ref_, ConvertUTF8ToJavaString(env, app_id),
                          ConvertUTF8ToJavaString(env, sender_ids[0]));
}

void GCMDriverAndroid::UnregisterImpl(const std::string& app_id) {
  NOTREACHED_IN_MIGRATION();
}

void GCMDriverAndroid::UnregisterWithSenderIdImpl(
    const std::string& app_id,
    const std::string& sender_id) {
  JNIEnv* env = AttachCurrentThread();

  recorder_.RecordUnregistrationSent(app_id);

  Java_GCMDriver_unregister(env, java_ref_,
                            ConvertUTF8ToJavaString(env, app_id),
                            ConvertUTF8ToJavaString(env, sender_id));
}

void GCMDriverAndroid::SendImpl(const std::string& app_id,
                                const std::string& receiver_id,
                                const OutgoingMessage& message) {
  NOTIMPLEMENTED();
}

void GCMDriverAndroid::RecordDecryptionFailure(const std::string& app_id,
                                               GCMDecryptionResult result) {
  recorder_.RecordDecryptionFailure(app_id, result);
}

}  // namespace gcm