1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "components/cronet/android/chromium_url_request_context.h"
6
7#include <string>
8
9#include "base/android/jni_android.h"
10#include "base/android/jni_string.h"
11#include "base/json/json_reader.h"
12#include "base/logging.h"
13#include "base/memory/scoped_ptr.h"
14#include "base/metrics/statistics_recorder.h"
15#include "base/values.h"
16#include "components/cronet/android/chromium_url_request.h"
17#include "components/cronet/android/url_request_adapter.h"
18#include "components/cronet/android/url_request_context_adapter.h"
19#include "components/cronet/url_request_context_config.h"
20#include "jni/ChromiumUrlRequestContext_jni.h"
21
22namespace {
23
24// Delegate of URLRequestContextAdapter that delivers callbacks to the Java
25// layer.
26class JniURLRequestContextAdapterDelegate
27    : public cronet::URLRequestContextAdapter::
28          URLRequestContextAdapterDelegate {
29 public:
30  JniURLRequestContextAdapterDelegate(JNIEnv* env, jobject owner)
31      : owner_(env->NewGlobalRef(owner)) {}
32
33  virtual void OnContextInitialized(
34      cronet::URLRequestContextAdapter* context) OVERRIDE {
35    JNIEnv* env = base::android::AttachCurrentThread();
36    cronet::Java_ChromiumUrlRequestContext_initNetworkThread(env, owner_);
37    // TODO(dplotnikov): figure out if we need to detach from the thread.
38    // The documentation says we should detach just before the thread exits.
39  }
40
41 protected:
42  virtual ~JniURLRequestContextAdapterDelegate() {
43    JNIEnv* env = base::android::AttachCurrentThread();
44    env->DeleteGlobalRef(owner_);
45  }
46
47 private:
48  jobject owner_;
49};
50
51}  // namespace
52
53namespace cronet {
54
55// Explicitly register static JNI functions.
56bool ChromiumUrlRequestContextRegisterJni(JNIEnv* env) {
57  return RegisterNativesImpl(env);
58}
59
60// Sets global user-agent to be used for all subsequent requests.
61static jlong CreateRequestContextAdapter(JNIEnv* env,
62                                         jobject object,
63                                         jobject context,
64                                         jstring user_agent,
65                                         jint log_level,
66                                         jstring config) {
67  std::string user_agent_string =
68      base::android::ConvertJavaStringToUTF8(env, user_agent);
69
70  std::string config_string =
71      base::android::ConvertJavaStringToUTF8(env, config);
72
73  scoped_ptr<base::Value> config_value(base::JSONReader::Read(config_string));
74  if (!config_value || !config_value->IsType(base::Value::TYPE_DICTIONARY)) {
75    DLOG(ERROR) << "Bad JSON: " << config_string;
76    return 0;
77  }
78
79  scoped_ptr<URLRequestContextConfig> context_config(
80      new URLRequestContextConfig());
81  base::JSONValueConverter<URLRequestContextConfig> converter;
82  if (!converter.Convert(*config_value, context_config.get())) {
83    DLOG(ERROR) << "Bad Config: " << config_value;
84    return 0;
85  }
86
87  // Set application context.
88  base::android::ScopedJavaLocalRef<jobject> scoped_context(env, context);
89  base::android::InitApplicationContext(env, scoped_context);
90
91  // TODO(mef): MinLogLevel is global, shared by all URLRequestContexts.
92  // Revisit this if each URLRequestContext would need an individual log level.
93  logging::SetMinLogLevel(static_cast<int>(log_level));
94
95  // TODO(dplotnikov): set application context.
96  URLRequestContextAdapter* adapter = new URLRequestContextAdapter(
97      new JniURLRequestContextAdapterDelegate(env, object), user_agent_string);
98  adapter->AddRef();  // Hold onto this ref-counted object.
99  adapter->Initialize(context_config.Pass());
100  return reinterpret_cast<jlong>(adapter);
101}
102
103// Releases native objects.
104static void ReleaseRequestContextAdapter(JNIEnv* env,
105                                         jobject object,
106                                         jlong urlRequestContextAdapter) {
107  URLRequestContextAdapter* adapter =
108      reinterpret_cast<URLRequestContextAdapter*>(urlRequestContextAdapter);
109  // TODO(mef): Revisit this from thread safety point of view: Can we delete a
110  // thread while running on that thread?
111  // URLRequestContextAdapter is a ref-counted object, and may have pending
112  // tasks,
113  // so we need to release it instead of deleting here.
114  adapter->Release();
115}
116
117// Starts recording statistics.
118static void InitializeStatistics(JNIEnv* env, jobject jcaller) {
119  base::StatisticsRecorder::Initialize();
120}
121
122// Gets current statistics with |filter| as a substring as JSON text (an empty
123// |filter| will include all registered histograms).
124static jstring GetStatisticsJSON(JNIEnv* env, jobject jcaller, jstring filter) {
125  std::string query = base::android::ConvertJavaStringToUTF8(env, filter);
126  std::string json = base::StatisticsRecorder::ToJSON(query);
127  return base::android::ConvertUTF8ToJavaString(env, json).Release();
128}
129
130// Starts recording NetLog into file with |fileName|.
131static void StartNetLogToFile(JNIEnv* env,
132                              jobject jcaller,
133                              jlong urlRequestContextAdapter,
134                              jstring fileName) {
135  URLRequestContextAdapter* adapter =
136      reinterpret_cast<URLRequestContextAdapter*>(urlRequestContextAdapter);
137  std::string file_name = base::android::ConvertJavaStringToUTF8(env, fileName);
138  adapter->StartNetLogToFile(file_name);
139}
140
141// Stops recording NetLog.
142static void StopNetLog(JNIEnv* env,
143                       jobject jcaller,
144                       jlong urlRequestContextAdapter) {
145  URLRequestContextAdapter* adapter =
146      reinterpret_cast<URLRequestContextAdapter*>(urlRequestContextAdapter);
147  adapter->StopNetLog();
148}
149
150}  // namespace cronet
151