blob: ac9cbeab2c6bf1b26082ed518e84b0461621a7c3 [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/bind.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 "content/public/browser/storage_partition.h"
#include "jni/ConnectivityChecker_jni.h"
#include "net/base/load_flags.h"
#include "net/http/http_status_code.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/cpp/simple_url_loader.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::BindOnce(&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:
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();
// Cancels the URLFetcher, and triggers the callback with a negative result
// and the timeout flag set.
void OnTimeout();
private:
void OnURLLoadComplete(scoped_refptr<net::HttpResponseHeaders> headers);
scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory_;
// 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<network::SimpleURLLoader> url_loader_;
// 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::OnURLLoadComplete(
scoped_refptr<net::HttpResponseHeaders> headers) {
if (is_being_destroyed_)
return;
is_being_destroyed_ = true;
DCHECK(url_loader_);
bool connected = headers && headers->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)
: shared_url_loader_factory_(
content::BrowserContext::GetDefaultStoragePartition(profile)
->GetURLLoaderFactoryForBrowserProcess()),
url_(url),
timeout_(timeout),
java_callback_(java_callback),
is_being_destroyed_(false) {}
void ConnectivityChecker::StartAsyncCheck() {
auto request = std::make_unique<network::ResourceRequest>();
request->url = url_;
request->allow_credentials = false;
request->load_flags = net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE;
url_loader_ = network::SimpleURLLoader::Create(std::move(request),
NO_TRAFFIC_ANNOTATION_YET);
url_loader_->DownloadHeadersOnly(
shared_url_loader_factory_.get(),
base::BindOnce(&ConnectivityChecker::OnURLLoadComplete,
base::Unretained(this)));
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_loader_.reset();
ExecuteCallback(java_callback_, CONNECTIVITY_CHECK_RESULT_TIMEOUT);
base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this);
}
} // namespace
void JNI_ConnectivityChecker_CheckConnectivity(
JNIEnv* env,
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<jstring>& j_url) {
GURL url(base::android::ConvertJavaStringToUTF8(env, j_url));
return url.is_valid();
}
} // namespace android
} // namespace chrome