Skip to content

Commit

Permalink
bluetooth: Android adapter can be created with and without Bluetooth …
Browse files Browse the repository at this point in the history
…permission.

This enables unit tests to run with Bluetooth permission,
by adding the Bluetooth permission to native_test.

Non-test applications will not have the Bluetooth permission,
and unit tests should also verify behavior
when the permission is not given. To enable this
createWithoutPermissionForTesting is added and
results in a state equivalent to when the permission
is not available.

Unit tests will be built in parallel for both when Bluetooth
permission exists and doesn't.

BUG=471536

Review URL: https://codereview.chromium.org/1129683002

Cr-Commit-Position: refs/heads/master@{#329212}
  • Loading branch information
scheib authored and Commit bot committed May 11, 2015
1 parent cc762f4 commit 30e8147
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@

package org.chromium.device.bluetooth;

import android.Manifest;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.PackageManager;

import org.chromium.base.CalledByNative;
Expand All @@ -20,23 +22,86 @@ final class BluetoothAdapter {
private static final String TAG = Log.makeTag("Bluetooth");

private final boolean mHasBluetoothPermission;
private android.bluetooth.BluetoothAdapter mAdapter;

@CalledByNative
private static BluetoothAdapter create(Context context) {
return new BluetoothAdapter(context);
}

@CalledByNative
private boolean hasBluetoothPermission() {
return mHasBluetoothPermission;
private static BluetoothAdapter createWithoutPermissionForTesting(Context context) {
Context contextWithoutPermission = new ContextWrapper(context) {
@Override
public int checkCallingOrSelfPermission(String permission) {
return PackageManager.PERMISSION_DENIED;
}
};
return new BluetoothAdapter(contextWithoutPermission);
}

// Constructs a BluetoothAdapter.
private BluetoothAdapter(Context context) {
mHasBluetoothPermission =
context.checkCallingOrSelfPermission(android.Manifest.permission.BLUETOOTH)
== PackageManager.PERMISSION_GRANTED;
context.checkCallingOrSelfPermission(Manifest.permission.BLUETOOTH)
== PackageManager.PERMISSION_GRANTED
&& context.checkCallingOrSelfPermission(Manifest.permission.BLUETOOTH_ADMIN)
== PackageManager.PERMISSION_GRANTED;
if (!mHasBluetoothPermission) {
Log.w(TAG, "Can not use bluetooth API, requires BLUETOOTH permission.");
Log.w(TAG,
"Bluetooth API disabled; BLUETOOTH and BLUETOOTH_ADMIN permissions required.");
return;
}

mAdapter = android.bluetooth.BluetoothAdapter.getDefaultAdapter();
if (mAdapter == null) Log.i(TAG, "No adapter found.");
}

@CalledByNative
private boolean hasBluetoothPermission() {
return mHasBluetoothPermission;
}

// ---------------------------------------------------------------------------------------------
// BluetoothAdapterAndroid.h interface:

@CalledByNative
private String getAddress() {
if (isPresent()) {
return mAdapter.getAddress();
} else {
return "";
}
}

@CalledByNative
private String getName() {
if (isPresent()) {
return mAdapter.getName();
} else {
return "";
}
}

@CalledByNative
private boolean isPresent() {
return mAdapter != null;
}

@CalledByNative
private boolean isPowered() {
return isPresent() && mAdapter.isEnabled();
}

@CalledByNative
private boolean isDiscoverable() {
return isPresent()
&& mAdapter.getScanMode()
== android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
}

@CalledByNative
private boolean isDiscovering() {
return isPresent() && mAdapter.isDiscovering();
}
}
37 changes: 25 additions & 12 deletions device/bluetooth/bluetooth_adapter_android.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
#include "device/bluetooth/bluetooth_adapter_android.h"

#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/sequenced_task_runner.h"
#include "base/single_thread_task_runner.h"
#include "base/thread_task_runner_handle.h"
#include "device/bluetooth/bluetooth_advertisement.h"
#include "jni/BluetoothAdapter_jni.h"

using base::android::AttachCurrentThread;
using base::android::ConvertJavaStringToUTF8;

namespace device {

Expand All @@ -25,6 +27,17 @@ base::WeakPtr<BluetoothAdapter> BluetoothAdapter::CreateAdapter(
base::WeakPtr<BluetoothAdapterAndroid>
BluetoothAdapterAndroid::CreateAdapter() {
BluetoothAdapterAndroid* adapter = new BluetoothAdapterAndroid();
adapter->j_bluetooth_adapter_.Reset(Java_BluetoothAdapter_create(
AttachCurrentThread(), base::android::GetApplicationContext()));
return adapter->weak_ptr_factory_.GetWeakPtr();
}

base::WeakPtr<BluetoothAdapterAndroid>
BluetoothAdapterAndroid::CreateAdapterWithoutPermissionForTesting() {
BluetoothAdapterAndroid* adapter = new BluetoothAdapterAndroid();
adapter->j_bluetooth_adapter_.Reset(
Java_BluetoothAdapter_createWithoutPermissionForTesting(
AttachCurrentThread(), base::android::GetApplicationContext()));
return adapter->weak_ptr_factory_.GetWeakPtr();
}

Expand All @@ -39,11 +52,13 @@ bool BluetoothAdapterAndroid::HasBluetoothPermission() const {
}

std::string BluetoothAdapterAndroid::GetAddress() const {
return address_;
return ConvertJavaStringToUTF8(Java_BluetoothAdapter_getAddress(
AttachCurrentThread(), j_bluetooth_adapter_.obj()));
}

std::string BluetoothAdapterAndroid::GetName() const {
return name_;
return ConvertJavaStringToUTF8(Java_BluetoothAdapter_getName(
AttachCurrentThread(), j_bluetooth_adapter_.obj()));
}

void BluetoothAdapterAndroid::SetName(const std::string& name,
Expand All @@ -58,13 +73,13 @@ bool BluetoothAdapterAndroid::IsInitialized() const {
}

bool BluetoothAdapterAndroid::IsPresent() const {
NOTIMPLEMENTED();
return false;
return Java_BluetoothAdapter_isPresent(AttachCurrentThread(),
j_bluetooth_adapter_.obj());
}

bool BluetoothAdapterAndroid::IsPowered() const {
NOTIMPLEMENTED();
return false;
return Java_BluetoothAdapter_isPowered(AttachCurrentThread(),
j_bluetooth_adapter_.obj());
}

void BluetoothAdapterAndroid::SetPowered(bool powered,
Expand All @@ -74,8 +89,8 @@ void BluetoothAdapterAndroid::SetPowered(bool powered,
}

bool BluetoothAdapterAndroid::IsDiscoverable() const {
NOTIMPLEMENTED();
return false;
return Java_BluetoothAdapter_isDiscoverable(AttachCurrentThread(),
j_bluetooth_adapter_.obj());
}

void BluetoothAdapterAndroid::SetDiscoverable(
Expand All @@ -86,8 +101,8 @@ void BluetoothAdapterAndroid::SetDiscoverable(
}

bool BluetoothAdapterAndroid::IsDiscovering() const {
NOTIMPLEMENTED();
return false;
return Java_BluetoothAdapter_isDiscovering(AttachCurrentThread(),
j_bluetooth_adapter_.obj());
}

void BluetoothAdapterAndroid::CreateRfcommService(
Expand Down Expand Up @@ -123,8 +138,6 @@ void BluetoothAdapterAndroid::RegisterAdvertisement(
}

BluetoothAdapterAndroid::BluetoothAdapterAndroid() : weak_ptr_factory_(this) {
j_bluetooth_adapter_.Reset(Java_BluetoothAdapter_create(
AttachCurrentThread(), base::android::GetApplicationContext()));
}

BluetoothAdapterAndroid::~BluetoothAdapterAndroid() {
Expand Down
4 changes: 4 additions & 0 deletions device/bluetooth/bluetooth_adapter_android.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterAndroid final
// Create a BluetoothAdapterAndroid instance.
static base::WeakPtr<BluetoothAdapterAndroid> CreateAdapter();

// Create a BluetoothAdapterAndroid instance without Bluetooth permission.
static base::WeakPtr<BluetoothAdapterAndroid>
CreateAdapterWithoutPermissionForTesting();

// Register C++ methods exposed to Java using JNI.
static bool RegisterJNI(JNIEnv* env);

Expand Down
31 changes: 30 additions & 1 deletion device/bluetooth/bluetooth_adapter_android_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,45 @@ namespace device {

class BluetoothAdapterAndroidTest : public testing::Test {
protected:
BluetoothAdapterAndroidTest() {
void InitWithPermission() {
adapter_ = BluetoothAdapterAndroid::CreateAdapter().get();
}

void InitWithoutPermission() {
adapter_ =
BluetoothAdapterAndroid::CreateAdapterWithoutPermissionForTesting()
.get();
}

scoped_refptr<BluetoothAdapterAndroid> adapter_;
};

TEST_F(BluetoothAdapterAndroidTest, Construct) {
InitWithPermission();
ASSERT_TRUE(adapter_.get());
EXPECT_TRUE(adapter_->HasBluetoothPermission());
if (!adapter_->IsPresent()) {
LOG(WARNING) << "Bluetooth adapter not present; skipping unit test.";
return;
}
EXPECT_GT(adapter_->GetAddress().length(), 0u);
EXPECT_GT(adapter_->GetName().length(), 0u);
EXPECT_TRUE(adapter_->IsPresent());
EXPECT_TRUE(adapter_->IsPowered());
EXPECT_FALSE(adapter_->IsDiscoverable());
EXPECT_FALSE(adapter_->IsDiscovering());
}

TEST_F(BluetoothAdapterAndroidTest, ConstructNoPermision) {
InitWithoutPermission();
ASSERT_TRUE(adapter_.get());
EXPECT_FALSE(adapter_->HasBluetoothPermission());
EXPECT_EQ(adapter_->GetAddress().length(), 0u);
EXPECT_EQ(adapter_->GetName().length(), 0u);
EXPECT_FALSE(adapter_->IsPresent());
EXPECT_FALSE(adapter_->IsPowered());
EXPECT_FALSE(adapter_->IsDiscoverable());
EXPECT_FALSE(adapter_->IsDiscovering());
}

} // namespace device
3 changes: 2 additions & 1 deletion testing/android/native_test/java/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ found in the LICENSE file.
android:versionName="1.0">

<uses-sdk android:minSdkVersion="16" android:targetSdkVersion="22" />

<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
Expand Down

0 comments on commit 30e8147

Please sign in to comment.