1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.googlecode.android_scripting.facade.wifi; 18 19 import android.app.Service; 20 import android.content.BroadcastReceiver; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.IntentFilter; 24 import android.content.pm.PackageManager; 25 import android.net.MacAddress; 26 import android.net.wifi.aware.PeerHandle; 27 import android.net.wifi.rtt.RangingRequest; 28 import android.net.wifi.rtt.RangingResult; 29 import android.net.wifi.rtt.RangingResultCallback; 30 import android.net.wifi.rtt.WifiRttManager; 31 import android.os.Bundle; 32 import android.os.Parcelable; 33 import android.os.RemoteException; 34 import android.os.WorkSource; 35 36 import com.android.internal.annotations.GuardedBy; 37 38 import libcore.util.HexEncoding; 39 40 import com.googlecode.android_scripting.facade.EventFacade; 41 import com.googlecode.android_scripting.facade.FacadeManager; 42 import com.googlecode.android_scripting.jsonrpc.RpcReceiver; 43 import com.googlecode.android_scripting.rpc.Rpc; 44 import com.googlecode.android_scripting.rpc.RpcOptional; 45 import com.googlecode.android_scripting.rpc.RpcParameter; 46 47 import org.json.JSONArray; 48 import org.json.JSONException; 49 50 import java.util.List; 51 52 /** 53 * Facade for RTTv2 manager. 54 */ 55 public class WifiRtt2ManagerFacade extends RpcReceiver { 56 private final Service mService; 57 private final EventFacade mEventFacade; 58 private final StateChangedReceiver mStateChangedReceiver; 59 60 private final Object mLock = new Object(); // lock access to the following vars 61 62 @GuardedBy("mLock") 63 private WifiRttManager mMgr; 64 65 @GuardedBy("mLock") 66 private int mNextRangingResultCallbackId = 1; 67 68 public WifiRtt2ManagerFacade(FacadeManager manager) { 69 super(manager); 70 mService = manager.getService(); 71 mEventFacade = manager.getReceiver(EventFacade.class); 72 73 mMgr = (WifiRttManager) mService.getSystemService(Context.WIFI_RTT_RANGING_SERVICE); 74 75 mStateChangedReceiver = new StateChangedReceiver(); 76 IntentFilter filter = new IntentFilter(WifiRttManager.ACTION_WIFI_RTT_STATE_CHANGED); 77 mService.registerReceiver(mStateChangedReceiver, filter); 78 } 79 80 @Override 81 public void shutdown() { 82 // empty 83 } 84 85 @Rpc(description = "Does the device support the Wi-Fi RTT feature?") 86 public Boolean doesDeviceSupportWifiRttFeature() { 87 return mService.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_RTT); 88 } 89 90 @Rpc(description = "Is Wi-Fi RTT Usage Enabled?") 91 public Boolean wifiIsRttAvailable() throws RemoteException { 92 synchronized (mLock) { 93 return mMgr.isAvailable(); 94 } 95 } 96 97 @Rpc(description = "The maximum number of peers permitted in a single RTT request") 98 public Integer wifiRttMaxPeersInRequest() { 99 return RangingRequest.getMaxPeers(); 100 } 101 102 /** 103 * Start Wi-Fi RTT ranging to an Acccess Point using the given scan results. Returns the id 104 * associated with the listener used for ranging. The ranging result event will be decorated 105 * with the listener id. 106 */ 107 @Rpc(description = "Start ranging to an Access Points.", returns = "Id of the listener " 108 + "associated with the started ranging.") 109 public Integer wifiRttStartRangingToAccessPoints( 110 @RpcParameter(name = "scanResults") JSONArray scanResults, 111 @RpcParameter(name = "uidsOverride", description = "Overrides for the UID") 112 @RpcOptional JSONArray uidsOverride) throws JSONException { 113 synchronized (mLock) { 114 int id = mNextRangingResultCallbackId++; 115 RangingResultCallback callback = new RangingResultCallbackFacade(id); 116 mMgr.startRanging(getWorkSource(uidsOverride), 117 new RangingRequest.Builder().addAccessPoints( 118 WifiJsonParser.getScanResults(scanResults)).build(), 119 mService.getMainExecutor(), callback); 120 return id; 121 } 122 } 123 124 @Rpc(description = "Start ranging to an Aware peer.", returns = "Id of the listener " 125 + "associated with the started ranging.") 126 public Integer wifiRttStartRangingToAwarePeerMac( 127 @RpcParameter(name = "peerMac") String peerMac, 128 @RpcParameter(name = "uidsOverride", description = "Overrides for the UID") 129 @RpcOptional JSONArray uidsOverride) throws JSONException { 130 synchronized (mLock) { 131 int id = mNextRangingResultCallbackId++; 132 RangingResultCallback callback = new RangingResultCallbackFacade(id); 133 byte[] peerMacBytes = HexEncoding.decode(peerMac); // since Aware returns string w/o ":" 134 mMgr.startRanging(getWorkSource(uidsOverride), 135 new RangingRequest.Builder().addWifiAwarePeer( 136 MacAddress.fromBytes(peerMacBytes)).build(), mService.getMainExecutor(), 137 callback); 138 return id; 139 } 140 } 141 142 @Rpc(description = "Start ranging to an Aware peer.", returns = "Id of the listener " 143 + "associated with the started ranging.") 144 public Integer wifiRttStartRangingToAwarePeerId( 145 @RpcParameter(name = "peer ID") Integer peerId, 146 @RpcParameter(name = "uidsOverride", description = "Overrides for the UID") 147 @RpcOptional JSONArray uidsOverride) throws JSONException { 148 synchronized (mLock) { 149 int id = mNextRangingResultCallbackId++; 150 RangingResultCallback callback = new RangingResultCallbackFacade(id); 151 mMgr.startRanging(getWorkSource(uidsOverride), 152 new RangingRequest.Builder().addWifiAwarePeer( 153 new PeerHandle(peerId)).build(), mService.getMainExecutor(), callback); 154 return id; 155 } 156 } 157 158 @Rpc(description = "Cancel ranging requests for the specified UIDs") 159 public void wifiRttCancelRanging(@RpcParameter(name = "uids", description = "List of UIDs") 160 @RpcOptional JSONArray uids) throws JSONException { 161 synchronized (mLock) { 162 mMgr.cancelRanging(getWorkSource(uids)); 163 } 164 } 165 166 private class RangingResultCallbackFacade extends RangingResultCallback { 167 private int mCallbackId; 168 169 RangingResultCallbackFacade(int callbackId) { 170 mCallbackId = callbackId; 171 } 172 173 @Override 174 public void onRangingFailure(int status) { 175 Bundle msg = new Bundle(); 176 msg.putInt("status", status); 177 mEventFacade.postEvent("WifiRttRangingFailure_" + mCallbackId, msg); 178 } 179 180 @Override 181 public void onRangingResults(List<RangingResult> results) { 182 Bundle msg = new Bundle(); 183 Parcelable[] resultBundles = new Parcelable[results.size()]; 184 for (int i = 0; i < results.size(); i++) { 185 resultBundles[i] = packRttResult(results.get(i)); 186 } 187 msg.putParcelableArray("Results", resultBundles); 188 mEventFacade.postEvent("WifiRttRangingResults_" + mCallbackId, msg); 189 } 190 } 191 192 // conversion utilities 193 private static Bundle packRttResult(RangingResult result) { 194 Bundle bundle = new Bundle(); 195 bundle.putInt("status", result.getStatus()); 196 if (result.getStatus() == RangingResult.STATUS_SUCCESS) { // only valid on SUCCESS 197 bundle.putInt("distanceMm", result.getDistanceMm()); 198 bundle.putInt("distanceStdDevMm", result.getDistanceStdDevMm()); 199 bundle.putInt("rssi", result.getRssi()); 200 bundle.putInt("numAttemptedMeasurements", result.getNumAttemptedMeasurements()); 201 bundle.putInt("numSuccessfulMeasurements", result.getNumSuccessfulMeasurements()); 202 bundle.putByteArray("lci", result.getLci()); 203 bundle.putByteArray("lcr", result.getLcr()); 204 bundle.putLong("timestamp", result.getRangingTimestampMillis()); 205 } 206 if (result.getPeerHandle() != null) { 207 bundle.putInt("peerId", result.getPeerHandle().peerId); 208 } 209 if (result.getMacAddress() != null) { 210 bundle.putByteArray("mac", result.getMacAddress().toByteArray()); 211 bundle.putString("macAsString", result.getMacAddress().toString()); 212 } 213 return bundle; 214 } 215 216 private static WorkSource getWorkSource(JSONArray uids) throws JSONException { 217 if (uids == null) { 218 return null; 219 } 220 WorkSource ws = new WorkSource(); 221 for (int i = 0; i < uids.length(); ++i) { 222 ws.add(uids.getInt(i)); 223 } 224 return ws; 225 } 226 227 class StateChangedReceiver extends BroadcastReceiver { 228 @Override 229 public void onReceive(Context c, Intent intent) { 230 boolean isAvailable = mMgr.isAvailable(); 231 mEventFacade.postEvent(isAvailable ? "WifiRttAvailable" : "WifiRttNotAvailable", 232 new Bundle()); 233 } 234 } 235 } 236