[Android] Android Studioでpermissionの指定が効かない
こむろです。札幌も暑いです。
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を利用してるとサクサク補完してくれるので、諸々入力をサボっていたのですがこんな罠が待っていたとは。