blob: 19a854ba558ec4770e06f30b9699a14a00e903de [file] [log] [blame]
// Copyright 2015 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 "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/android/scoped_java_ref.h"
#include "base/location.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_android.h"
#include "components/data_use_measurement/core/data_use_user_data.h"
#include "jni/ConnectivityChecker_jni.h"
#include "net/base/load_flags.h"
#include "net/http/http_status_code.h"
#include "net/url_request/url_fetcher.h"
#include "net/url_request/url_fetcher_delegate.h"
#include "net/url_request/url_request_context_getter.h"
#include "net/url_request/url_request_status.h"
#include "url/gurl.h"
using base::android::JavaParamRef;
namespace chrome {
namespace android {
namespace {
// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.feedback
// GENERATED_JAVA_PREFIX_TO_STRIP: CONNECTIVITY_CHECK_RESULT_
enum ConnectivityCheckResult {
CONNECTIVITY_CHECK_RESULT_UNKNOWN = 0,
CONNECTIVITY_CHECK_RESULT_CONNECTED = 1,
CONNECTIVITY_CHECK_RESULT_NOT_CONNECTED = 2,
CONNECTIVITY_CHECK_RESULT_TIMEOUT = 3,
CONNECTIVITY_CHECK_RESULT_ERROR = 4,
CONNECTIVITY_CHECK_RESULT_END = 5
};
void ExecuteCallback(const base::android::JavaRef<jobject>& callback,
ConnectivityCheckResult result) {
CHECK(result >= CONNECTIVITY_CHECK_RESULT_UNKNOWN);
CHECK(result < CONNECTIVITY_CHECK_RESULT_END);
Java_ConnectivityChecker_executeCallback(base::android::AttachCurrentThread(),
callback, result);
}
void JNI_ConnectivityChecker_PostCallback(
JNIEnv* env,
const base::android::JavaRef<jobject>& j_callback,
ConnectivityCheckResult result) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::Bind(&ExecuteCallback,
base::android::ScopedJavaGlobalRef<jobject>(j_callback),
result));
}
// A utility class for checking if the device is currently connected to the
// Internet.
class ConnectivityChecker : public net::URLFetcherDelegate {
public:
ConnectivityChecker(Profile* profile,
const GURL& url,
const base::TimeDelta& timeout,
const base::android::JavaRef<jobject>& java_callback);
// Kicks off the asynchronous connectivity check. When the request has
// completed, |this| is deleted.
void StartAsyncCheck();
// net::URLFetcherDelegate implementation:
void OnURLFetchComplete(const net::URLFetcher* source) override;
// Cancels the URLFetcher, and triggers the callback with a negative result
// and the timeout flag set.
void OnTimeout();
private:
// The context in which the connectivity check is performed.
net::URLRequestContextGetter* request_context_;
// The URL to connect to.
const GURL& url_;
// How long to wait for a response before giving up.
const base::TimeDelta timeout_;
// Holds the Java object which will get the callback with the result.
base::android::ScopedJavaGlobalRef<jobject> java_callback_;
// The URLFetcher that executes the connectivity check.
std::unique_ptr<net::URLFetcher> url_fetcher_;
// Whether |this| is already being destroyed, at which point the callback
// has already happened, and no further action should be taken.
bool is_being_destroyed_;
std::unique_ptr<base::OneShotTimer> expiration_timer_;
};
void ConnectivityChecker::OnURLFetchComplete(const net::URLFetcher* source) {
if (is_being_destroyed_)
return;
is_being_destroyed_ = true;
DCHECK_EQ(url_fetcher_.get(), source);
net::URLRequestStatus status = source->GetStatus();
int response_code = source->GetResponseCode();
bool connected = status.is_success() && response_code == net::HTTP_NO_CONTENT;
if (connected) {
ExecuteCallback(java_callback_, CONNECTIVITY_CHECK_RESULT_CONNECTED);
} else {
ExecuteCallback(java_callback_, CONNECTIVITY_CHECK_RESULT_NOT_CONNECTED);
}
base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this);
}
ConnectivityChecker::ConnectivityChecker(
Profile* profile,
const GURL& url,
const base::TimeDelta& timeout,
const base::android::JavaRef<jobject>& java_callback)
: request_context_(profile->GetRequestContext()),
url_(url),
timeout_(timeout),
java_callback_(java_callback),
is_being_destroyed_(false) {
}
void ConnectivityChecker::StartAsyncCheck() {
url_fetcher_ = net::URLFetcher::Create(url_, net::URLFetcher::GET, this);
data_use_measurement::DataUseUserData::AttachToFetcher(
url_fetcher_.get(),
data_use_measurement::DataUseUserData::FEEDBACK_UPLOADER);
url_fetcher_->SetRequestContext(request_context_);
url_fetcher_->SetStopOnRedirect(true);
url_fetcher_->SetAutomaticallyRetryOn5xx(false);
url_fetcher_->SetAutomaticallyRetryOnNetworkChanges(0);
url_fetcher_->SetLoadFlags(net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE |
net::LOAD_DO_NOT_SAVE_COOKIES |
net::LOAD_DO_NOT_SEND_COOKIES |
net::LOAD_DO_NOT_SEND_AUTH_DATA);
url_fetcher_->Start();
expiration_timer_.reset(new base::OneShotTimer());
expiration_timer_->Start(FROM_HERE, timeout_, this,
&ConnectivityChecker::OnTimeout);
}
void ConnectivityChecker::OnTimeout() {
if (is_being_destroyed_)
return;
is_being_destroyed_ = true;
url_fetcher_.reset();
ExecuteCallback(java_callback_, CONNECTIVITY_CHECK_RESULT_TIMEOUT);
base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this);
}
} // namespace
void JNI_ConnectivityChecker_CheckConnectivity(
JNIEnv* env,
const JavaParamRef<jclass>& clazz,
const JavaParamRef<jobject>& j_profile,
const JavaParamRef<jstring>& j_url,
jlong j_timeout_ms,
const JavaParamRef<jobject>& j_callback) {
Profile* profile = ProfileAndroid::FromProfileAndroid(j_profile);
if (!profile) {
JNI_ConnectivityChecker_PostCallback(env, j_callback,
CONNECTIVITY_CHECK_RESULT_ERROR);
return;
}
GURL url(base::android::ConvertJavaStringToUTF8(env, j_url));
if (!url.is_valid()) {
JNI_ConnectivityChecker_PostCallback(env, j_callback,
CONNECTIVITY_CHECK_RESULT_ERROR);
return;
}
// This object will be deleted when the connectivity check has completed.
ConnectivityChecker* connectivity_checker = new ConnectivityChecker(
profile, url, base::TimeDelta::FromMilliseconds(j_timeout_ms),
j_callback);
connectivity_checker->StartAsyncCheck();
}
jboolean JNI_ConnectivityChecker_IsUrlValid(
JNIEnv* env,
const JavaParamRef<jclass>& clazz,
const JavaParamRef<jstring>& j_url) {
GURL url(base::android::ConvertJavaStringToUTF8(env, j_url));
return url.is_valid();
}
} // namespace android
} // namespace chrome