1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126

android_webview / browser / variations / variations_seed_loader.cc [blame]

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

#include <errno.h>
#include <fcntl.h>
#include <jni.h>
#include <memory>
#include <string>

#include "android_webview/browser/variations/variations_seed_loader.h"

#include "android_webview/proto/aw_variations_seed.pb.h"
#include "base/android/jni_string.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"

// Must come after all headers that specialize FromJniType() / ToJniType().
#include "android_webview/browser_jni_headers/VariationsSeedLoader_jni.h"

using base::android::ConvertJavaStringToUTF8;
using base::android::ConvertUTF8ToJavaString;
using base::android::JavaParamRef;

namespace android_webview {

AwVariationsSeed* g_seed = nullptr;

namespace {
bool IsSeedValid(AwVariationsSeed* seed) {
  // Empty or incomplete protos should be considered invalid. An empty seed
  // file is expected when we request a seed from the service, but no new seed
  // is available. In that case, an empty seed file will have been created, but
  // never written to.
  if (!seed->has_signature()) {
    LOG(ERROR) << "Seed missing signature.";
    return false;
  } else if (!seed->has_date()) {
    LOG(ERROR) << "Seed missing date.";
    return false;
  } else if (!seed->has_country()) {
    LOG(ERROR) << "Seed missing country.";
    return false;
  } else if (!seed->has_is_gzip_compressed()) {
    LOG(ERROR) << "Seed not compressed.";
    return false;
  } else if (!seed->has_seed_data()) {
    LOG(ERROR) << "Seed missing data.";
    return false;
  }
  return true;
}
}  // namespace

static jboolean JNI_VariationsSeedLoader_ParseAndSaveSeedProto(
    JNIEnv* env,
    std::string& seed_path) {
  // Parse the proto.
  std::unique_ptr<AwVariationsSeed> seed =
      std::make_unique<AwVariationsSeed>(AwVariationsSeed::default_instance());

  int native_fd = open(seed_path.c_str(), O_RDONLY);
  if (native_fd == -1) {
    PLOG(INFO) << "Failed to open file for reading.";
    return false;
  }

  base::ScopedFD seed_fd(native_fd);
  if (!seed_fd.get()) {
    LOG(ERROR) << "Failed to create seed file descriptor.";
    return false;
  }

  if (!seed->ParseFromFileDescriptor(seed_fd.get())) {
    LOG(ERROR) << "Falied to parse seed file.";
    return false;
  }

  if (IsSeedValid(seed.get())) {
    g_seed = seed.release();
    return true;
  } else {
    return false;
  }
}

static jboolean JNI_VariationsSeedLoader_ParseAndSaveSeedProtoFromByteArray(
    JNIEnv* env,
    const JavaParamRef<jbyteArray>& seed_as_bytes) {
  // Parse the proto.
  std::unique_ptr<AwVariationsSeed> seed =
      std::make_unique<AwVariationsSeed>(AwVariationsSeed::default_instance());
  jbyte* src_bytes = env->GetByteArrayElements(seed_as_bytes, nullptr);
  if (!seed->ParseFromArray(src_bytes,
                            env->GetArrayLength(seed_as_bytes.obj()))) {
    LOG(ERROR) << "Failed to parse seed file.";
    return false;
  }

  if (IsSeedValid(seed.get())) {
    g_seed = seed.release();
    return true;
  } else {
    return false;
  }
}

static jlong JNI_VariationsSeedLoader_GetSavedSeedDate(JNIEnv* env) {
  return g_seed ? g_seed->date() : 0;
}

std::unique_ptr<AwVariationsSeed> TakeSeed() {
  std::unique_ptr<AwVariationsSeed> seed(g_seed);
  g_seed = nullptr;
  return seed;
}

void CacheSeedFreshness(long freshness) {
  JNIEnv* env = jni_zero::AttachCurrentThread();
  Java_VariationsSeedLoader_cacheSeedFreshness(env, freshness);
}

}  // namespace android_webview