这里我们将对Android P版本的室内定位功能做个详细的说明
1)原理
在提供硬件支持的 Android P 设备上,应用可以使用全新的 RTT API 来测量与附近支持 RTT 的 Wi-Fi 接入点 (AP) 的距离。
2)要求
2.1)启用定位并开启了 Wi-Fi 扫描(在 Settings > Location 下)
2.2)具有 ACCESS_FINE_LOCATION 权限。
设备不需要连接至 AP 即可使用 RTT. 为保证隐私性,只有手机可以确定与 AP 的距离;AP 不具备该信息
3)Android P支持IEEE802.11mcWi-Fi协议(即Wi-FiRound-Trip-Time,RTT),利用此项技术及可以进行室内定位,因此为了使用此项技术,只有在硬件支持的设备上,应用才可以使用最新的RTT API以测量附近具有RTT功能的Wi-FiAP,我们可以使用uses-feature来标注这一特性
<uses-feature android:name="android.hardware.wifi.rtt" />
先看下这个标签的一些扩展
<uses-feature android:name="string"
android:required=["true" | "false"]
android:glEsVersion="integer" />
这里的required表示是否需要这个特征,ture表示需要这个硬件特征,才能正常工作;false表示即使手机没有这样的硬件特征,只是功能不全罢了, 但是app还是可以使用的
uses-feature主要是给应用市场的,应用市场通过读取该属性,来决定手机是否能够看见该App
举个例子:
例如一个app在uses-feature中声明了需要摄像头,且required为true,那么应用市场在分发app时就不会将该app分发给那些没有摄像头的设备上,一个没有摄像头的设备也不能通过应用市场搜索和下载到该app。但是如果用户通过其他渠道(例如官网)下载该app对应的apk文件到某个设备上,在该设备上安装此apk时,系统并不会检查uses-feature所声明的软硬件特征是否满足,如果该设备没有摄像头,同样可以安装该app。
同时uses-feature声明对应用的运行也不会产生影响(除非应用自己在代码中去判断某项feature是否满足)。这点和uses-permission是不一样的,Android系统会读取应用的uses-permission,并对应用的运行产生影响;
具体代码如下
package com.hwj.android.learning;
import android.annotation.SuppressLint;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.net.wifi.WifiManager;
import android.net.wifi.rtt.RangingRequest;
import android.net.wifi.rtt.RangingResult;
import android.net.wifi.rtt.RangingResultCallback;
import android.net.wifi.rtt.WifiRttManager;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import java.util.List;
/**
* Created by huangwangjian on 2018/5/7.
* 功能描述: WIFI RTT 室内定位测试类
*/
public class WiFiRtt {
private Context mContext;
public WiFiRtt(Context c){
this.mContext = c;
IntentFilter wifiFileter = new IntentFilter();
wifiFileter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
wifiFileter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
wifiFileter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
mContext.registerReceiver(new WifiChangeReceiver(), wifiFileter);
}
public class WifiChangeReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)){
useWifiRtt();
}
}
}
private WifiManager mWifiManager;
private WifiRttManager mWifiRttManager;
private WifiRttHandler mWifiRttHandler;
/*
* 室内定位主要使用WifiRttManager.java
* 该类主要利用 IEEE802.11mc Wi-Fi Round Trip Time (RTT) technology去侦测手机和Ap之间的距离
* 主要api为 startRanging,其作用是测量AP的距离,因为测试需要对多个ap侦测,是一个批量侦测行为
* 顾我们会用到以下Api:addAccessPoint(ScanResult)和addAccessPoints(List)来表示侦测这些AP
*
* startRanging接口Requires the ACCESS_COARSE_LOCATION, CHANGE_WIFI_STATE and ACCESS_WIFI_STATE permissions.
* startRanging接口参数详解
* 第一个参数:表示发起测距请求,必须不为null
* 第二个参数:表示请求有结果之后的回调
*/
@SuppressLint("WrongConstant")
private void useWifiRtt(){
mWifiRttHandler = new WifiRttHandler();
mWifiManager = (WifiManager)mContext.getSystemService(Context.WIFI_SERVICE);
boolean hasRtt = mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_RTT);
Log.d("hwj","**useWifiRtt**" + hasRtt);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) {
mWifiRttManager = (WifiRttManager)mContext.getSystemService(Context.WIFI_RTT_RANGING_SERVICE);
RangingRequest.Builder builder = new RangingRequest.Builder();
if(mWifiManager != null){
builder.addAccessPoints(mWifiManager.getScanResults());
}
if(mWifiRttManager != null){
mWifiRttManager.startRanging(builder.build(),new RangingResultCallback(){
@Override
public void onRangingFailure(int i) {
}
/**
* @param list:返回的测距的ap
*/
@Override
public void onRangingResults(List<RangingResult> list) {
}
},mWifiRttHandler);
}
}
}
private class WifiRttHandler extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
}
}
package com.hwj.android.learning;
import android.app.Activity;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;
public class AndroidPDemoActivity extends Activity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_android_p_demo_layout);
new WiFiRtt(this);
}
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.hwj.android.learning">
<uses-feature android:name="android.hardware.wifi.rtt" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<uses-sdk
android:targetSdkVersion="27"></uses-sdk>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".AndroidPDemoActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>