Commit 039b06c6 authored by Owen Min's avatar Owen Min Committed by Commit Bot

Add PolicyService JNI bridge for Clank

Create PolicyServiceAndroid and PolicyServiceObserverAndroid class so
that the Java part can listen to the policy initialization event.

Bug: 1108118
Change-Id: I7766808291eef6e0deab3a2d3f7badd2d64b6d29
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2327286
Commit-Queue: Owen Min <zmin@chromium.org>
Reviewed-by: default avatarSky Malice <skym@chromium.org>
Reviewed-by: default avatarWenyu Fu <wenyufu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#795740}
parent ad9eb1c8
......@@ -2837,6 +2837,7 @@ static_library("browser") {
"platform_util_android.cc",
"policy/cloud/user_policy_signin_service_mobile.cc",
"policy/cloud/user_policy_signin_service_mobile.h",
"policy/policy_service_factory_android.cc",
"prerender/external_prerender_handler_android.cc",
"prerender/external_prerender_handler_android.h",
"previews/android/previews_android_bridge.cc",
......@@ -2927,6 +2928,7 @@ static_library("browser") {
"//chrome/browser/password_check/android",
"//chrome/browser/password_check/android/internal:jni_headers",
"//chrome/browser/payments/android:jni_headers",
"//chrome/browser/policy/android:jni_headers",
"//chrome/browser/privacy:jni_headers",
"//chrome/browser/safety_check/android",
"//chrome/browser/share",
......
# Copyright 2020 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.
import("//build/config/android/rules.gni")
_jni_sources =
[ "java/src/org/chromium/chrome/browser/policy/PolicyServiceFactory.java" ]
android_library("java") {
deps = [
"//base:base_java",
"//base:jni_java",
"//chrome/browser/profiles/android:java",
"//components/policy/android:policy_java",
]
annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
sources = _jni_sources
}
generate_jni("jni_headers") {
sources = _jni_sources
}
// Copyright 2020 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.chrome.browser.policy;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.NativeMethods;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.policy.PolicyService;
/**
* Get the PolicyService instance. Note that the associated C++ instance won't
* notify its deletion. It's caller's responsibility to make sure the instance
* is still valid.
*/
@JNINamespace("policy::android")
public class PolicyServiceFactory {
/**
* Returns the PolicyService instance that contains browser policies.
* The associated C++ instance is deleted during shutdown.
*/
public static PolicyService getGlobalPolicyService() {
return PolicyServiceFactoryJni.get().getGlobalPolicyService();
}
/**
* Returns the PolicyService instance that contains |profile|'s policies.
* The associated C++ instance is deleted during shutdown or {@link Profile}
* deletion.
*/
public static PolicyService getProfilePolicyService(Profile profile) {
return PolicyServiceFactoryJni.get().getProfilePolicyService(profile);
}
@NativeMethods
public interface Natives {
PolicyService getGlobalPolicyService();
PolicyService getProfilePolicyService(Profile profile);
}
}
// Copyright 2020 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 "chrome/browser/policy/android/jni_headers/PolicyServiceFactory_jni.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/policy/chrome_browser_policy_connector.h"
#include "chrome/browser/policy/profile_policy_connector.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_android.h"
#include "components/policy/core/common/android/policy_service_android.h"
#include "components/policy/core/common/policy_service.h"
namespace policy {
namespace android {
base::android::ScopedJavaLocalRef<jobject>
JNI_PolicyServiceFactory_GetGlobalPolicyService(JNIEnv* env) {
return g_browser_process->policy_service()
->GetPolicyServiceAndroid()
->GetJavaObject();
}
base::android::ScopedJavaLocalRef<jobject>
JNI_PolicyServiceFactory_GetProfilePolicyService(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& j_profile) {
Profile* profile = ProfileAndroid::FromProfileAndroid(j_profile);
DCHECK(profile);
return profile->GetProfilePolicyConnector()
->policy_service()
->GetPolicyServiceAndroid()
->GetJavaObject();
}
} // namespace android
} // namespace policy
......@@ -343,6 +343,7 @@ test("components_unittests") {
"//components/paint_preview/browser/android:java",
"//components/paint_preview/player/android:unit_tests",
"//components/permissions/android:java",
"//components/policy/android:native_test_support_java",
"//components/policy/android:policy_java",
"//components/signin/core/browser",
"//components/signin/core/browser/android:java",
......
......@@ -7,6 +7,7 @@ import("//build/config/android/rules.gni")
_jni_sources = [
"java/src/org/chromium/policy/CombinedPolicyProvider.java",
"java/src/org/chromium/policy/PolicyConverter.java",
"java/src/org/chromium/policy/PolicyService.java",
]
android_library("policy_java") {
......@@ -67,3 +68,26 @@ java_library("components_policy_junit_tests") {
"//third_party/mockito:mockito_java",
]
}
_test_jni_sources =
[ "javatests/src/org/chromium/policy/test/PolicyServiceTestSupporter.java" ]
android_library("native_test_support_java") {
testonly = true
deps = [
":policy_java",
"//base:base_java",
"//base:jni_java",
"//third_party/junit",
"//third_party/mockito:mockito_java",
]
annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
sources = _test_jni_sources
}
generate_jni("test_jni_headers") {
testonly = true
visibility = [ "//components/policy/*" ]
sources = _test_jni_sources
}
// Copyright 2020 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.policy;
import org.chromium.base.CollectionUtil;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.NativeClassQualifiedName;
import org.chromium.base.annotations.NativeMethods;
import java.util.HashSet;
import java.util.Set;
/**
* Wrapper of the native PolicyService class in the Java layer.
*
* It only supports the Chrome policy domain but not any extension policy. More
* documentation can be found in
* //components/policy/core/common/policy_service.h
*
* Native pointer is owned by the C++ instance.
*
* The class only provides a subset of native class features for now, other
* functions will be added once needed.
*/
@JNINamespace("policy::android")
public class PolicyService {
private long mNativePolicyService;
private final Set<Observer> mObservers = new HashSet<Observer>();
/**
* Observer interface for observing PolicyService change for Chrome policy
* domain.
*/
public interface Observer {
/**
* Invoked when Chome policy domain is initialized. Observer must be
* added before the naitve PolicyService initialization being finished.
* Use {@link #isInitializationComplete} to check the initialization
* state before listening to this event.
*/
void onPolicyServiceInitialized();
}
/**
* @param observer The {@link Observer} to be notified for Chrome policy
* update.
*/
public void addObserver(Observer observer) {
if (mObservers.isEmpty()) {
PolicyServiceJni.get().addObserver(mNativePolicyService, PolicyService.this);
}
mObservers.add(observer);
}
/**
* @param observer The {@link Observer} to no longer be notified for Chrome
* policy update.
*/
public void removeObserver(Observer observer) {
mObservers.remove(observer);
if (mObservers.isEmpty()) {
PolicyServiceJni.get().removeObserver(mNativePolicyService, PolicyService.this);
}
}
/**
* Returns true if Chrome policy domain has been initialized.
*/
public boolean isInitializationComplete() {
return PolicyServiceJni.get().isInitializationComplete(
mNativePolicyService, PolicyService.this);
}
/**
* Pass the onPolicyServiceInitialized event to the |mObservers|.
*/
@CalledByNative
private void onPolicyServiceInitialized() {
CollectionUtil.forEach(mObservers, observer -> observer.onPolicyServiceInitialized());
}
@CalledByNative
private PolicyService(long nativePolicyService) {
mNativePolicyService = nativePolicyService;
}
@NativeMethods
public interface Natives {
@NativeClassQualifiedName("PolicyServiceAndroid")
void addObserver(long nativePolicyService, PolicyService caller);
@NativeClassQualifiedName("PolicyServiceAndroid")
void removeObserver(long nativePolicyService, PolicyService caller);
@NativeClassQualifiedName("PolicyServiceAndroid")
boolean isInitializationComplete(long nativePolicyService, PolicyService caller);
}
}
// Copyright 2020 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.policy.test;
import static org.mockito.Mockito.times;
import org.junit.Assert;
import org.mockito.Mockito;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.policy.PolicyService;
import java.util.ArrayList;
import java.util.List;
/**
* Native unit test helper for class {@link PolicyService}
*
* It's used by native unit tests in:
* components/policy/android/core/common/policy_service_android_unittest.cc
*/
@JNINamespace("policy::android")
public class PolicyServiceTestSupporter {
private List<PolicyService.Observer> mObservers = new ArrayList<>();
PolicyService mPolicyService;
@CalledByNative
private PolicyServiceTestSupporter(PolicyService policyService) {
mPolicyService = policyService;
}
@CalledByNative
private void verifyIsInitalizationComplete(boolean expected) {
Assert.assertEquals(expected, mPolicyService.isInitializationComplete());
}
@CalledByNative
private int addObserver() {
mObservers.add(Mockito.mock(PolicyService.Observer.class));
mPolicyService.addObserver(mObservers.get(mObservers.size() - 1));
return mObservers.size() - 1;
}
@CalledByNative
private void removeObserver(int index) {
mPolicyService.removeObserver(mObservers.get(index));
}
@CalledByNative
private void verifyObserverCalled(int index, int cnt) {
Mockito.verify(mObservers.get(index), times(cnt)).onPolicyServiceInitialized();
}
@CalledByNative
private void verifyNoMoreInteractions() {
for (PolicyService.Observer observer : mObservers) {
Mockito.verifyNoMoreInteractions(observer);
}
}
}
......@@ -235,6 +235,13 @@ source_set("internal") {
"policy_load_status.h",
]
}
if (is_android) {
sources += [
"android/policy_service_android.cc",
"android/policy_service_android.h",
]
deps += [ "//components/policy/android:jni_headers" ]
}
if (is_chromeos) {
deps += [ "//chromeos/system" ]
sources += [
......@@ -436,7 +443,9 @@ source_set("unit_tests") {
]
}
if (!is_android) {
if (is_android) {
sources += [ "android/policy_service_android_unittest.cc" ]
} else {
sources += [ "async_policy_provider_unittest.cc" ]
}
if (!is_android && !is_ios) {
......@@ -492,6 +501,9 @@ source_set("unit_tests") {
if (is_chromeos) {
deps += [ "//chromeos/system" ]
}
if (is_android) {
deps += [ "//components/policy/android:test_jni_headers" ]
}
}
if (is_win || is_linux || is_chromeos) {
......
// Copyright 2020 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 "components/policy/core/common/android/policy_service_android.h"
#include "base/android/jni_android.h"
#include "components/policy/android/jni_headers/PolicyService_jni.h"
namespace policy {
namespace android {
// PolicyServiceAndroid
PolicyServiceAndroid::PolicyServiceAndroid(PolicyService* policy_service)
: policy_service_(policy_service) {}
PolicyServiceAndroid::~PolicyServiceAndroid() = default;
void PolicyServiceAndroid::AddObserver(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& caller) {
policy_service_->AddObserver(POLICY_DOMAIN_CHROME, this);
}
void PolicyServiceAndroid::RemoveObserver(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& caller) {
policy_service_->RemoveObserver(POLICY_DOMAIN_CHROME, this);
}
void PolicyServiceAndroid::OnPolicyServiceInitialized(PolicyDomain domain) {
DCHECK_EQ(POLICY_DOMAIN_CHROME, domain);
DCHECK(java_ref_);
Java_PolicyService_onPolicyServiceInitialized(
base::android::AttachCurrentThread(),
base::android::ScopedJavaLocalRef<jobject>(java_ref_));
}
bool PolicyServiceAndroid::IsInitializationComplete(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& caller) {
return policy_service_->IsInitializationComplete(POLICY_DOMAIN_CHROME);
}
base::android::ScopedJavaLocalRef<jobject>
PolicyServiceAndroid::GetJavaObject() {
JNIEnv* env = base::android::AttachCurrentThread();
if (!java_ref_) {
java_ref_.Reset(
Java_PolicyService_Constructor(env, reinterpret_cast<intptr_t>(this)));
}
return base::android::ScopedJavaLocalRef<jobject>(java_ref_);
}
} // namespace android
} // namespace policy
// Copyright 2020 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 COMPONENTS_POLICY_CORE_COMMON_ANDROID_POLICY_SERVICE_ANDROID_H_
#define COMPONENTS_POLICY_CORE_COMMON_ANDROID_POLICY_SERVICE_ANDROID_H_
#include <jni.h>
#include <memory>
#include "base/android/jni_weak_ref.h"
#include "base/android/scoped_java_ref.h"
#include "components/policy/core/common/policy_service.h"
#include "components/policy/policy_export.h"
namespace policy {
namespace android {
// PolicyService bridge class that is used for Android. It's owned by the main
// PolicyService instance.
// Note that it only support the Chrome policy domain but not any extension
// policy.
class POLICY_EXPORT PolicyServiceAndroid : public PolicyService::Observer {
public:
PolicyServiceAndroid(PolicyService* policy_service);
PolicyServiceAndroid(const PolicyServiceAndroid&) = delete;
PolicyServiceAndroid& operator=(const PolicyServiceAndroid&) = delete;
~PolicyServiceAndroid() override;
void AddObserver(JNIEnv* env,
const base::android::JavaParamRef<jobject>& caller);
void RemoveObserver(JNIEnv* env,
const base::android::JavaParamRef<jobject>& caller);
bool IsInitializationComplete(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& caller);
// PolicyService::Observer implementation.
// Pass the event to the Java observers.
void OnPolicyServiceInitialized(PolicyDomain domain) override;
base::android::ScopedJavaLocalRef<jobject> GetJavaObject();
private:
PolicyService* policy_service_;
base::android::ScopedJavaGlobalRef<jobject> java_ref_;
};
} // namespace android
} // namespace policy
#endif // COMPONENTS_POLICY_CORE_COMMON_ANDROID_POLICY_SERVICE_ANDROID_H_
// Copyright 2020 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 "components/policy/core/common/android/policy_service_android.h"
#include <jni.h>
#include "base/android/jni_android.h"
#include "base/android/scoped_java_ref.h"
#include "components/policy/android/test_jni_headers/PolicyServiceTestSupporter_jni.h"
#include "components/policy/core/common/mock_policy_service.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::Return;
namespace policy {
namespace android {
// Tests the bridge class PolicyServiceAndroid. It uses the Java helper class in
// //components/policy/android/javatest/.../test/PolicyServiceTestSupporter.java
class PolicyServiceAndroidTest : public ::testing::Test {
public:
PolicyServiceAndroidTest() = default;
~PolicyServiceAndroidTest() override {
Java_PolicyServiceTestSupporter_verifyNoMoreInteractions(env_, j_support_);
}
JNIEnv* env_ = base::android::AttachCurrentThread();
MockPolicyService policy_service_;
PolicyServiceAndroid policy_service_android_ = {&policy_service_};
base::android::ScopedJavaLocalRef<jobject> j_support_ =
Java_PolicyServiceTestSupporter_Constructor(
env_,
policy_service_android_.GetJavaObject());
};
TEST_F(PolicyServiceAndroidTest, IsInitializationComplete) {
EXPECT_CALL(policy_service_, IsInitializationComplete(POLICY_DOMAIN_CHROME))
.Times(2)
.WillOnce(Return(false))
.WillOnce(Return(true));
EXPECT_CALL(policy_service_,
IsInitializationComplete(POLICY_DOMAIN_EXTENSIONS))
.Times(0);
EXPECT_CALL(policy_service_,
IsInitializationComplete(POLICY_DOMAIN_SIGNIN_EXTENSIONS))
.Times(0);
Java_PolicyServiceTestSupporter_verifyIsInitalizationComplete(
env_, j_support_, false);
Java_PolicyServiceTestSupporter_verifyIsInitalizationComplete(
env_, j_support_, true);
::testing::Mock::VerifyAndClearExpectations(&policy_service_);
}
TEST_F(PolicyServiceAndroidTest, OneObserver) {
EXPECT_CALL(policy_service_,
AddObserver(POLICY_DOMAIN_CHROME, &policy_service_android_))
.Times(1);
int observer_id =
Java_PolicyServiceTestSupporter_addObserver(env_, j_support_);
policy_service_android_.OnPolicyServiceInitialized(POLICY_DOMAIN_CHROME);
Java_PolicyServiceTestSupporter_verifyObserverCalled(
env_, j_support_, /*index*/ 0, /*times*/ 1);
EXPECT_CALL(policy_service_,
RemoveObserver(POLICY_DOMAIN_CHROME, &policy_service_android_))
.Times(1);
Java_PolicyServiceTestSupporter_removeObserver(env_, j_support_, observer_id);
::testing::Mock::VerifyAndClearExpectations(&policy_service_);
}
TEST_F(PolicyServiceAndroidTest, MultipleObservers) {
// When multiple observers are added in Java, only one observer will be
// created in C++.
EXPECT_CALL(policy_service_,
AddObserver(POLICY_DOMAIN_CHROME, &policy_service_android_))
.Times(1);
int observer1 = Java_PolicyServiceTestSupporter_addObserver(env_, j_support_);
int observer2 = Java_PolicyServiceTestSupporter_addObserver(env_, j_support_);
// And we still observing the PolicyService as long as there is one Java
// observer.
Java_PolicyServiceTestSupporter_removeObserver(env_, j_support_, observer2);
::testing::Mock::VerifyAndClearExpectations(&policy_service_);
// Trigger the event and only the activated Java observer get notified.
policy_service_android_.OnPolicyServiceInitialized(POLICY_DOMAIN_CHROME);
Java_PolicyServiceTestSupporter_verifyObserverCalled(env_, j_support_,
observer1, /*times*/ 1);
Java_PolicyServiceTestSupporter_verifyObserverCalled(env_, j_support_,
observer2, /*times*/ 0);
// Remove the last Java observers and triggers the C++ observer cleanup too.
EXPECT_CALL(policy_service_,
RemoveObserver(POLICY_DOMAIN_CHROME, &policy_service_android_))
.Times(1);
Java_PolicyServiceTestSupporter_removeObserver(env_, j_support_, observer1);
::testing::Mock::VerifyAndClearExpectations(&policy_service_);
}
} // namespace android
} // namespace policy
......@@ -5,6 +5,7 @@
#ifndef COMPONENTS_POLICY_CORE_COMMON_MOCK_POLICY_SERVICE_H_
#define COMPONENTS_POLICY_CORE_COMMON_MOCK_POLICY_SERVICE_H_
#include "build/build_config.h"
#include "components/policy/core/common/policy_service.h"
#include "testing/gmock/include/gmock/gmock.h"
......@@ -45,6 +46,10 @@ class MockPolicyService : public PolicyService {
MOCK_CONST_METHOD1(GetPolicies, const PolicyMap&(const PolicyNamespace&));
MOCK_CONST_METHOD1(IsInitializationComplete, bool(PolicyDomain domain));
MOCK_METHOD1(RefreshPolicies, void(base::OnceClosure));
#if defined(OS_ANDROID)
MOCK_METHOD0(GetPolicyServiceAndroid, android::PolicyServiceAndroid*());
#endif
};
} // namespace policy
......
......@@ -11,6 +11,7 @@
#include "base/callback.h"
#include "base/macros.h"
#include "base/observer_list_types.h"
#include "build/build_config.h"
#include "components/policy/core/common/policy_map.h"
#include "components/policy/core/common/policy_namespace.h"
#include "components/policy/policy_export.h"
......@@ -19,6 +20,12 @@ namespace policy {
class ConfigurationPolicyProvider;
#if defined(OS_ANDROID)
namespace android {
class PolicyServiceAndroid;
}
#endif
// The PolicyService merges policies from all available sources, taking into
// account their priorities. Policy clients can retrieve policy for their domain
// and register for notifications on policy updates.
......@@ -36,7 +43,7 @@ class POLICY_EXPORT PolicyService {
// and |current| contains the current values.
virtual void OnPolicyUpdated(const PolicyNamespace& ns,
const PolicyMap& previous,
const PolicyMap& current) = 0;
const PolicyMap& current) {}
// Invoked at most once for each |domain|, when the PolicyService becomes
// ready. If IsInitializationComplete() is false, then this will be invoked
......@@ -97,6 +104,11 @@ class POLICY_EXPORT PolicyService {
// |callback| is invoked once every source has reloaded its policies, and
// GetPolicies() is guaranteed to return the updated values at that point.
virtual void RefreshPolicies(base::OnceClosure callback) = 0;
#if defined(OS_ANDROID)
// Get the PolicyService JNI bridge instance.
virtual android::PolicyServiceAndroid* GetPolicyServiceAndroid() = 0;
#endif
};
// A registrar that only observes changes to particular policies within the
......
......@@ -26,6 +26,10 @@
#include "components/policy/core/common/values_util.h"
#include "components/policy/policy_constants.h"
#if defined(OS_ANDROID)
#include "components/policy/core/common/android/policy_service_android.h"
#endif
namespace policy {
namespace {
......@@ -204,6 +208,15 @@ void PolicyServiceImpl::RefreshPolicies(base::OnceClosure callback) {
}
}
#if defined(OS_ANDROID)
android::PolicyServiceAndroid* PolicyServiceImpl::GetPolicyServiceAndroid() {
if (!policy_service_android_)
policy_service_android_ =
std::make_unique<android::PolicyServiceAndroid>(this);
return policy_service_android_.get();
}
#endif
void PolicyServiceImpl::UnthrottleInitialization() {
DCHECK(thread_checker_.CalledOnValidThread());
if (!initialization_throttled_)
......
......@@ -16,6 +16,7 @@
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/threading/thread_checker.h"
#include "build/build_config.h"
#include "components/policy/core/common/configuration_policy_provider.h"
#include "components/policy/core/common/policy_bundle.h"
#include "components/policy/core/common/policy_migrator.h"
......@@ -26,6 +27,12 @@ namespace policy {
class PolicyMap;
#if defined(OS_ANDROID)
namespace android {
class PolicyServiceAndroid;
}
#endif
class POLICY_EXPORT PolicyServiceImpl
: public PolicyService,
public ConfigurationPolicyProvider::Observer {
......@@ -61,6 +68,9 @@ class POLICY_EXPORT PolicyServiceImpl
const PolicyMap& GetPolicies(const PolicyNamespace& ns) const override;
bool IsInitializationComplete(PolicyDomain domain) const override;
void RefreshPolicies(base::OnceClosure callback) override;
#if defined(OS_ANDROID)
android::PolicyServiceAndroid* GetPolicyServiceAndroid() override;
#endif
// If this PolicyServiceImpl has been created using
// |CreateWithThrottledInitialization|, calling UnthrottleInitialization will
......@@ -152,6 +162,10 @@ class POLICY_EXPORT PolicyServiceImpl
// initialization signal.
bool initialization_throttled_;
#if defined(OS_ANDROID)
std::unique_ptr<android::PolicyServiceAndroid> policy_service_android_;
#endif
// Used to verify thread-safe usage.
base::ThreadChecker thread_checker_;
......
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