Commit 29849a47 authored by Yuwei Huang's avatar Yuwei Huang Committed by Commit Bot

[CRD Android] Android JNI OAuthTokenGetter implementation

This CL implements OAuthTokenGetter for Android. This is part of the
project of implementing a centralized access token getter in client C++
code, so that multiple components like telemetry logger, ICE config
fetcher, and session connector can use the same token fetcher without
requesting access token back and forth through JNI.

Bug: 780736
Change-Id: I265fe963ef6f9e1720b3a7e3f209704be8686957
Reviewed-on: https://chromium-review.googlesource.com/760184
Commit-Queue: Yuwei Huang <yuweih@chromium.org>
Reviewed-by: default avatarLambros Lambrou <lambroslambrou@chromium.org>
Cr-Commit-Position: refs/heads/master@{#515467}
parent 02bee6d1
......@@ -16,6 +16,7 @@ generate_jni("jni_headers") {
"java/src/org/chromium/chromoting/jni/Client.java",
"java/src/org/chromium/chromoting/jni/GlDisplay.java",
"java/src/org/chromium/chromoting/jni/JniInterface.java",
"java/src/org/chromium/chromoting/jni/JniOAuthTokenGetter.java",
"java/src/org/chromium/chromoting/jni/TouchEventData.java",
]
jni_package = "remoting"
......
......@@ -71,6 +71,7 @@ template("remoting_android_client_java_tmpl") {
"jni/ConnectionListener.java",
"jni/GlDisplay.java",
"jni/JniInterface.java",
"jni/JniOAuthTokenGetter.java",
"jni/TouchEventData.java",
]
......@@ -89,6 +90,8 @@ template("remoting_android_client_java_tmpl") {
"//ui/android:ui_utils_java",
]
srcjar_deps = [ "//remoting/client/jni:jni_enums" ]
if (defined(invoker.play_services_package)) {
deps += [
"${invoker.play_services_package}:google_play_services_auth_base_java",
......
......@@ -41,6 +41,7 @@ import org.chromium.chromoting.help.HelpSingleton;
import org.chromium.chromoting.jni.Client;
import org.chromium.chromoting.jni.ConnectionListener;
import org.chromium.chromoting.jni.JniInterface;
import org.chromium.chromoting.jni.JniOAuthTokenGetter;
import java.util.ArrayList;
import java.util.Arrays;
......@@ -620,6 +621,7 @@ public class Chromoting extends AppCompatActivity implements ConnectionListener,
mAccountSwitcher.getView().announceForAccessibility(logInAnnouncement);
}
mAccount = accountName;
JniOAuthTokenGetter.setAccount(accountName);
JniInterface.setAccountForLogging(accountName);
// The current host list is no longer valid for the new account, so clear the list.
......
......@@ -39,6 +39,7 @@ public class JniInterface {
*/
public static void loadLibrary(Context context) {
ContextUtils.initApplicationContext(context.getApplicationContext());
JniOAuthTokenGetter.setContext(context);
sLoggerTokenConsumer = new OAuthTokenConsumer(context.getApplicationContext(), TOKEN_SCOPE);
try {
System.loadLibrary(LIBRARY_NAME);
......
// Copyright 2017 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.
package org.chromium.chromoting.jni;
import android.annotation.SuppressLint;
import android.content.Context;
import org.chromium.base.Log;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.chromoting.Preconditions;
import org.chromium.chromoting.base.OAuthTokenFetcher;
import org.chromium.chromoting.base.OAuthTokenFetcher.Callback;
import org.chromium.chromoting.base.OAuthTokenFetcher.Error;
/**
* The Java implementation of the JniOAuthTokenGetter class. Used by C++ code to request OAuth
* token.
* Note that both context and account must be set before the token getter is being used.
*/
@JNINamespace("remoting")
public class JniOAuthTokenGetter {
private static final String TAG = "Chromoting";
private static final String TOKEN_SCOPE = "oauth2:https://www.googleapis.com/auth/chromoting";
@SuppressLint("StaticFieldLeak")
private static Context sContext;
private static String sAccount;
private static String sLatestToken;
public static void setContext(Context context) {
Preconditions.notNull(context);
// Always store the application context so that we don't leak the activity context by
// accident.
sContext = context.getApplicationContext();
}
public static void setAccount(String account) {
Preconditions.notNull(account);
sAccount = account;
}
@CalledByNative
private static void fetchAuthToken(long callbackPtr) {
Preconditions.notNull(sContext);
Preconditions.notNull(sAccount);
new OAuthTokenFetcher(sContext, sAccount, TOKEN_SCOPE, new Callback() {
@Override
public void onTokenFetched(String token) {
sLatestToken = token;
nativeResolveOAuthTokenCallback(
callbackPtr, OAuthTokenStatus.SUCCESS, sAccount, token);
}
@Override
public void onError(Error error) {
Log.e(TAG, "Failed to fetch token. Error: ", error);
int status;
switch (error) {
case NETWORK:
status = OAuthTokenStatus.NETWORK_ERROR;
break;
case UI:
case UNEXPECTED:
case INTERRUPTED:
status = OAuthTokenStatus.AUTH_ERROR;
break;
default:
assert false : "Unreached";
status = -1;
}
nativeResolveOAuthTokenCallback(callbackPtr, status, null, null);
}
}).fetch();
}
@CalledByNative
private static void invalidateCache() {
if (sLatestToken == null || sLatestToken.isEmpty()) {
return;
}
Preconditions.notNull(sContext);
Preconditions.notNull(sAccount);
new OAuthTokenFetcher(sContext, sAccount, TOKEN_SCOPE, new Callback() {
@Override
public void onTokenFetched(String token) {
sLatestToken = token;
}
@Override
public void onError(Error error) {
Log.e(TAG, "Failed to clear token. Error: ", error);
}
}).clearAndFetch(sLatestToken);
}
private static native void nativeResolveOAuthTokenCallback(
long callbackPtr, int status, String userEmail, String accessToken);
}
......@@ -33,6 +33,8 @@ shared_library("remoting_client_jni") {
"jni_client.h",
"jni_gl_display_handler.cc",
"jni_gl_display_handler.h",
"jni_oauth_token_getter.cc",
"jni_oauth_token_getter.h",
"jni_pairing_secret_fetcher.cc",
"jni_pairing_secret_fetcher.h",
"jni_runtime_delegate.cc",
......@@ -48,3 +50,9 @@ shared_library("remoting_client_jni") {
configs += [ "//remoting/build/config:enable_webrtc_remoting_client" ]
assert_no_deps = [ "//third_party/ffmpeg:*" ]
}
java_cpp_enum("jni_enums") {
sources = [
"jni_oauth_token_getter.h",
]
}
// Copyright 2017 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 "remoting/client/jni/jni_oauth_token_getter.h"
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/android/scoped_java_ref.h"
#include "jni/JniOAuthTokenGetter_jni.h"
using base::android::ConvertJavaStringToUTF8;
using base::android::JavaParamRef;
namespace remoting {
static void ResolveOAuthTokenCallback(JNIEnv* env,
const JavaParamRef<jclass>& clazz,
jlong callback_ptr,
jint jni_status,
const JavaParamRef<jstring>& user_email,
const JavaParamRef<jstring>& token) {
auto* callback =
reinterpret_cast<OAuthTokenGetter::TokenCallback*>(callback_ptr);
OAuthTokenGetter::Status status;
switch (static_cast<JniOAuthTokenGetter::JniStatus>(jni_status)) {
case JniOAuthTokenGetter::JNI_STATUS_SUCCESS:
status = OAuthTokenGetter::SUCCESS;
break;
case JniOAuthTokenGetter::JNI_STATUS_NETWORK_ERROR:
status = OAuthTokenGetter::NETWORK_ERROR;
break;
case JniOAuthTokenGetter::JNI_STATUS_AUTH_ERROR:
status = OAuthTokenGetter::AUTH_ERROR;
break;
default:
NOTREACHED();
return;
}
callback->Run(status,
user_email.is_null() ? "" : ConvertJavaStringToUTF8(user_email),
token.is_null() ? "" : ConvertJavaStringToUTF8(token));
delete callback;
}
JniOAuthTokenGetter::JniOAuthTokenGetter() {
DETACH_FROM_THREAD(thread_checker_);
}
JniOAuthTokenGetter::~JniOAuthTokenGetter() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
}
void JniOAuthTokenGetter::CallWithToken(const TokenCallback& on_access_token) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
JNIEnv* env = base::android::AttachCurrentThread();
TokenCallback* callback_copy = new TokenCallback(on_access_token);
Java_JniOAuthTokenGetter_fetchAuthToken(
env, reinterpret_cast<intptr_t>(callback_copy));
}
void JniOAuthTokenGetter::InvalidateCache() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
JNIEnv* env = base::android::AttachCurrentThread();
Java_JniOAuthTokenGetter_invalidateCache(env);
}
} // namespace remoting
// Copyright 2017 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.
#ifndef REMOTING_CLIENT_JNI_JNI_OAUTH_TOKEN_GETTER_
#define REMOTING_CLIENT_JNI_JNI_OAUTH_TOKEN_GETTER_
#include "base/macros.h"
#include "base/threading/thread_checker.h"
#include "remoting/base/oauth_token_getter.h"
namespace remoting {
// The OAuthTokenGetter implementation on Android using JNI. Please also read
// documentations in JniOAuthTokenGetter.java.
// This class must be used and destroyed on the same thread after it is created.
class JniOAuthTokenGetter : public OAuthTokenGetter {
public:
// This is for generating the Java enum counterpart. Please keep this in sync
// with OAuthTokenGetter::Status.
// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chromoting.jni
// GENERATED_JAVA_CLASS_NAME_OVERRIDE: OAuthTokenStatus
enum JniStatus {
JNI_STATUS_SUCCESS,
JNI_STATUS_NETWORK_ERROR,
JNI_STATUS_AUTH_ERROR,
};
JniOAuthTokenGetter();
~JniOAuthTokenGetter() override;
// OAuthTokenGetter overrides.
void CallWithToken(const TokenCallback& on_access_token) override;
void InvalidateCache() override;
private:
THREAD_CHECKER(thread_checker_);
DISALLOW_COPY_AND_ASSIGN(JniOAuthTokenGetter);
};
} // namespace remoting
#endif // REMOTING_CLIENT_JNI_JNI_OAUTH_TOKEN_GETTER_
......@@ -18,7 +18,9 @@
#include "jni/JniInterface_jni.h"
#include "remoting/base/chromium_url_request.h"
#include "remoting/base/url_request_context_getter.h"
#include "remoting/client/jni/jni_oauth_token_getter.h"
#include "remoting/client/jni/jni_touch_event_data.h"
#include "remoting/client/oauth_token_getter_proxy.h"
using base::android::ConvertJavaStringToUTF8;
using base::android::ConvertUTF8ToJavaString;
......@@ -66,6 +68,8 @@ JniRuntimeDelegate* JniRuntimeDelegate::GetInstance() {
JniRuntimeDelegate::JniRuntimeDelegate() {
runtime_ = ChromotingClientRuntime::GetInstance();
token_getter_ = std::make_unique<OAuthTokenGetterProxy>(
std::make_unique<JniOAuthTokenGetter>(), runtime_->ui_task_runner());
}
JniRuntimeDelegate::~JniRuntimeDelegate() {
......@@ -107,10 +111,7 @@ void JniRuntimeDelegate::RequestAuthTokenForLogger() {
}
OAuthTokenGetter* JniRuntimeDelegate::token_getter() {
// TODO(yuweih): Implement this. This is currently only used if the client
// uses WebRTC.
NOTIMPLEMENTED();
return nullptr;
return token_getter_.get();
}
void JniRuntimeDelegate::DetachFromVmAndSignal(base::WaitableEvent* waiter) {
......
......@@ -6,6 +6,7 @@
#define REMOTING_CLIENT_JNI_JNI_RUNTIME_DELEGATE_H_
#include <jni.h>
#include <memory>
#include <string>
#include "base/android/scoped_java_ref.h"
......@@ -58,6 +59,7 @@ class JniRuntimeDelegate : public ChromotingClientRuntime::Delegate {
void DetachFromVmAndSignal(base::WaitableEvent* waiter);
ChromotingClientRuntime* runtime_;
std::unique_ptr<OAuthTokenGetter> token_getter_;
friend struct base::DefaultSingletonTraits<JniRuntimeDelegate>;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment