この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
こむろです。札幌も暑いです。
Permissionの指定が機能しない
自分の現在の環境はこんなかんじです。
- OS: Mac OSX Yosemite 10.10.3
- IDE: Android Studio 1.2 Beta
Bluetooth LEの機能を利用したアプリケーションを作成するため、以下のようなAndroidManifest.xmlを作成しました。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="sample.android.classmethod.jp.sampleble" >
<uses-permission android:name="ANDROID.PERMISSION.INTERNET" />
<uses-permission android:name="ANDROID.PERMISSION.BLUETOOTH" />
<uses-permission android:name="ANDROID.PERMISSION.BLUETOOTH_ADMIN" />
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Permissionの部分は、Android Studioの補完機能を使って出てきてくる値をそのまま利用しています。
表示ではandroid.permission.となっているにも関わらず、AndroidManifestに実際記入されるのは全て大文字です。しかし特に気にせずそのまま実装を続けていました。
実装したFragmentクラスはこんな感じです。
/**
* A placeholder fragment containing a simple view.
*/
public class MainActivityFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater!!.inflate(R.layout.fragment_main, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
val startScanButton:Button = getActivity().findViewById(R.id.button) as Button
startScanButton.setOnClickListener { v ->
val adapter: BluetoothAdapter? = if (!getActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(v.getContext(), "Bluetooth LEに対応していません.", Toast.LENGTH_SHORT).show()
null
} else {
getBluetoothAdapter(getActivity())
}
adapter?.startLeScan(leScanCallback)
}
val stopScanButton:Button = getActivity().findViewById(R.id.button2) as Button
stopScanButton.setOnClickListener { v ->
val adapter: BluetoothAdapter? = if (!getActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
null
} else {
getBluetoothAdapter(getActivity())
}
adapter?.stopLeScan(leScanCallback)
}
}
val leScanCallback = object:BluetoothAdapter.LeScanCallback {
override fun onLeScan(device: BluetoothDevice?, rssi: Int, scanRecord: ByteArray?) {
Log.d("MainActivityFragment", "BluetoothDevice: " + device?.getAddress() + ", RSSI: " + rssi)
}
}
}
まあ、特別難しいことをしていません。Start Scan Buttonが押されたら、LeScanを実行しているだけです。
実行するとエラーで落ちる
このコードですが、実行すると落ちました。Stacktraceを見ると以下の通り。
java.lang.SecurityException: Need BLUETOOTH permission: Neither user 10003 nor current process has android.permission.BLUETOOTH.
at android.os.Parcel.readException(Parcel.java:1547)
at android.os.Parcel.readException(Parcel.java:1499)
at android.bluetooth.IBluetooth$Stub$Proxy.getState(IBluetooth.java:878)
at android.bluetooth.BluetoothAdapter.getState(BluetoothAdapter.java:583)
at android.bluetooth.BluetoothAdapter.startLeScan(BluetoothAdapter.java:1824)
Logcat「やれやれ。Bluetoothを使いたいならPermissionを指定してくれなきゃ困るよ。キミィ」という言葉が聞こえてきそうです。指定してるよ!
小文字の箇所は小文字で指定せよ
一番怪しいのはAndroidManifest.xmlファイルです。以下のように修正します。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="sample.android.classmethod.jp.sampleble" >
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
これで起動し直します。
04-30 18:34:36.526 9835-9852/sample.android.classmethod.jp.sampleble D/BluetoothLeScanner﹕ onClientRegistered() - status=0 clientIf=5
04-30 18:34:36.580 9835-9852/sample.android.classmethod.jp.sampleble D/BluetoothLeScanner﹕ onScanResult() - ScanResult{mDevice=49:E2:98:EE:7E:6D, mScanRecord=ScanRecord [mAdvertiseFlags=6, mServiceUuids=null, mManufacturerSpecificData={76=[12, 14, 0, -118, -42, 46, -115, 93, 111, -18, -31, -111, 81, 37, -117, 4]}, mServiceData={}, mTxPowerLevel=-2147483648, mDeviceName=null], mRssi=-57, mTimestampNanos=40659127925103}
04-30 18:34:36.582 9835-9835/sample.android.classmethod.jp.sampleble D/MainActivityFragment﹕ BluetoothDevice: 49:E2:98:EE:7E:6D, RSSI: -57
04-30 18:34:36.592 9835-9852/sample.android.classmethod.jp.sampleble D/BluetoothLeScanner﹕ onScanResult() - ScanResult{mDevice=73:33:9D:47:E7:A9, mScanRecord=ScanRecord [mAdvertiseFlags=26, mServiceUuids=null, mManufacturerSpecificData={76=[12, 14, 0, -92, 0, 80, -112, 17, -41, 113, 125, 122, 38, 120, 39, 5]}, mServiceData={}, mTxPowerLevel=-2147483648, mDeviceName=null], mRssi=-82, mTimestampNanos=40659139928957}
04-30 18:34:36.592 9835-9835/sample.android.classmethod.jp.sampleble D/MainActivityFragment﹕ BluetoothDevice: 73:33:9D:47:E7:A9, RSSI: -82
無事BLEのスキャンが実行されたようです。
結論
Android Studioの補完を信じすぎずに自分で書きましょう!
Android Studioを利用してるとサクサク補完してくれるので、諸々入力をサボっていたのですがこんな罠が待っていたとは。