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