1 /* 2 * Copyright (C) 2018 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 android.net.wifi.rtt; 18 19 import static android.Manifest.permission.ACCESS_FINE_LOCATION; 20 import static android.Manifest.permission.ACCESS_WIFI_STATE; 21 import static android.Manifest.permission.CHANGE_WIFI_STATE; 22 import static android.Manifest.permission.LOCATION_HARDWARE; 23 import static android.Manifest.permission.NEARBY_WIFI_DEVICES; 24 25 import android.annotation.CallbackExecutor; 26 import android.annotation.FlaggedApi; 27 import android.annotation.NonNull; 28 import android.annotation.Nullable; 29 import android.annotation.RequiresPermission; 30 import android.annotation.SdkConstant; 31 import android.annotation.StringDef; 32 import android.annotation.SystemApi; 33 import android.annotation.SystemService; 34 import android.content.Context; 35 import android.net.wifi.ScanResult; 36 import android.net.wifi.WifiManager; 37 import android.os.Binder; 38 import android.os.Bundle; 39 import android.os.RemoteException; 40 import android.os.WorkSource; 41 import android.util.Log; 42 43 import com.android.modules.utils.build.SdkLevel; 44 import com.android.wifi.flags.Flags; 45 46 import java.lang.annotation.Retention; 47 import java.lang.annotation.RetentionPolicy; 48 import java.util.List; 49 import java.util.concurrent.Executor; 50 51 /** 52 * This class provides the primary API for measuring distance (range) to other devices using the 53 * IEEE 802.11mc Wi-Fi Round Trip Time (RTT) technology. 54 * <p> 55 * The devices which can be ranged include: 56 * <li>Access Points (APs) 57 * <li>Wi-Fi Aware peers 58 * <p> 59 * Ranging requests are triggered using 60 * {@link #startRanging(RangingRequest, Executor, RangingResultCallback)}. Results (in case of 61 * successful operation) are returned in the {@link RangingResultCallback#onRangingResults(List)} 62 * callback. 63 * <p> 64 * Wi-Fi RTT may not be usable at some points, e.g. when Wi-Fi is disabled. To validate that 65 * the functionality is available use the {@link #isAvailable()} function. To track 66 * changes in RTT usability register for the {@link #ACTION_WIFI_RTT_STATE_CHANGED} 67 * broadcast. Note that this broadcast is not sticky - you should register for it and then 68 * check the above API to avoid a race condition. 69 */ 70 @SystemService(Context.WIFI_RTT_RANGING_SERVICE) 71 public class WifiRttManager { 72 private static final String TAG = "WifiRttManager"; 73 private static final boolean VDBG = false; 74 75 private final Context mContext; 76 private final IWifiRttManager mService; 77 78 /** 79 * Broadcast intent action to indicate that the state of Wi-Fi RTT availability has changed. 80 * Use the {@link #isAvailable()} to query the current status. 81 * This broadcast is <b>not</b> sticky, use the {@link #isAvailable()} API after registering 82 * the broadcast to check the current state of Wi-Fi RTT. 83 * <p>Note: The broadcast is only delivered to registered receivers - no manifest registered 84 * components will be launched. 85 */ 86 @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) 87 public static final String ACTION_WIFI_RTT_STATE_CHANGED = 88 "android.net.wifi.rtt.action.WIFI_RTT_STATE_CHANGED"; 89 90 /** 91 * Bundle key to access if one-sided Wi-Fi RTT is supported. When it is not supported, only 92 * two-sided RTT can be performed, which requires responder supports IEEE 802.11mc and this can 93 * be determined by the method {@link ScanResult#is80211mcResponder()} 94 */ 95 public static final String CHARACTERISTICS_KEY_BOOLEAN_ONE_SIDED_RTT = "key_one_sided_rtt"; 96 /** 97 * Bundle key to access if getting the Location Configuration Information(LCI) from responder is 98 * supported. 99 * @see ResponderLocation 100 */ 101 public static final String CHARACTERISTICS_KEY_BOOLEAN_LCI = "key_lci"; 102 /** 103 * Bundle key to access if getting the Location Civic Report(LCR) from responder is supported. 104 * @see ResponderLocation 105 */ 106 public static final String CHARACTERISTICS_KEY_BOOLEAN_LCR = "key_lcr"; 107 108 /** 109 * Bundle key to access if device supports to be a responder in station mode 110 */ 111 public static final String CHARACTERISTICS_KEY_BOOLEAN_STA_RESPONDER = "key_sta_responder"; 112 113 /** 114 * Bundle key to access if device supports to be a IEEE 802.11az non-trigger based initiator 115 */ 116 @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) 117 public static final String CHARACTERISTICS_KEY_BOOLEAN_NTB_INITIATOR = "key_ntb_initiator"; 118 119 /** @hide */ 120 @StringDef(prefix = { "CHARACTERISTICS_KEY_"}, value = { 121 CHARACTERISTICS_KEY_BOOLEAN_ONE_SIDED_RTT, 122 CHARACTERISTICS_KEY_BOOLEAN_LCI, 123 CHARACTERISTICS_KEY_BOOLEAN_LCR, 124 CHARACTERISTICS_KEY_BOOLEAN_STA_RESPONDER, 125 CHARACTERISTICS_KEY_BOOLEAN_NTB_INITIATOR, 126 }) 127 @Retention(RetentionPolicy.SOURCE) 128 public @interface RttCharacteristicsKey {} 129 130 /** @hide */ WifiRttManager(@onNull Context context, @NonNull IWifiRttManager service)131 public WifiRttManager(@NonNull Context context, @NonNull IWifiRttManager service) { 132 mContext = context; 133 mService = service; 134 } 135 136 /** 137 * Returns the current status of RTT API: whether or not RTT is available. To track 138 * changes in the state of RTT API register for the 139 * {@link #ACTION_WIFI_RTT_STATE_CHANGED} broadcast. 140 * <p>Note: availability of RTT does not mean that the app can use the API. The app's 141 * permissions and platform Location Mode are validated at run-time. 142 * 143 * @return A boolean indicating whether the app can use the RTT API at this time (true) or 144 * not (false). 145 */ isAvailable()146 public boolean isAvailable() { 147 try { 148 return mService.isAvailable(); 149 } catch (RemoteException e) { 150 throw e.rethrowFromSystemServer(); 151 } 152 } 153 154 /** 155 * Initiate a request to range to a set of devices specified in the {@link RangingRequest}. 156 * Results will be returned in the {@link RangingResultCallback} set of callbacks. 157 * <p> 158 * Ranging request with only Wifi Aware peers can be performed with either 159 * {@link android.Manifest.permission#NEARBY_WIFI_DEVICES} with 160 * android:usesPermissionFlags="neverForLocation", or 161 * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. All other types of ranging requests 162 * require {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. 163 * 164 * @param request A request specifying a set of devices whose distance measurements are 165 * requested. 166 * @param executor The Executor on which to run the callback. 167 * @param callback A callback for the result of the ranging request. 168 */ 169 @RequiresPermission(allOf = {ACCESS_FINE_LOCATION, CHANGE_WIFI_STATE, ACCESS_WIFI_STATE, 170 NEARBY_WIFI_DEVICES}) startRanging(@onNull RangingRequest request, @NonNull @CallbackExecutor Executor executor, @NonNull RangingResultCallback callback)171 public void startRanging(@NonNull RangingRequest request, 172 @NonNull @CallbackExecutor Executor executor, @NonNull RangingResultCallback callback) { 173 startRanging(null, request, executor, callback); 174 } 175 176 /** 177 * Initiate a request to range to a set of devices specified in the {@link RangingRequest}. 178 * Results will be returned in the {@link RangingResultCallback} set of callbacks. 179 * <p> 180 * Ranging request with only Wifi Aware peers can be performed with either 181 * {@link android.Manifest.permission#NEARBY_WIFI_DEVICES} with 182 * android:usesPermissionFlags="neverForLocation", or 183 * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. All other types of ranging requests 184 * require {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. 185 * 186 * @param workSource A mechanism to specify an alternative work-source for the request. 187 * @param request A request specifying a set of devices whose distance measurements are 188 * requested. 189 * @param executor The Executor on which to run the callback. 190 * @param callback A callback for the result of the ranging request. 191 * 192 * @hide 193 */ 194 @SystemApi 195 @RequiresPermission(allOf = {LOCATION_HARDWARE, ACCESS_FINE_LOCATION, CHANGE_WIFI_STATE, 196 ACCESS_WIFI_STATE, NEARBY_WIFI_DEVICES}, conditional = true) startRanging(@ullable WorkSource workSource, @NonNull RangingRequest request, @NonNull @CallbackExecutor Executor executor, @NonNull RangingResultCallback callback)197 public void startRanging(@Nullable WorkSource workSource, @NonNull RangingRequest request, 198 @NonNull @CallbackExecutor Executor executor, @NonNull RangingResultCallback callback) { 199 if (VDBG) { 200 Log.v(TAG, "startRanging: workSource=" + workSource + ", request=" + request 201 + ", callback=" + callback + ", executor=" + executor); 202 } 203 204 if (executor == null) { 205 throw new IllegalArgumentException("Null executor provided"); 206 } 207 if (callback == null) { 208 throw new IllegalArgumentException("Null callback provided"); 209 } 210 211 Binder binder = new Binder(); 212 try { 213 Bundle extras = new Bundle(); 214 if (SdkLevel.isAtLeastS()) { 215 extras.putParcelable(WifiManager.EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE, 216 mContext.getAttributionSource()); 217 } 218 mService.startRanging(binder, mContext.getOpPackageName(), 219 mContext.getAttributionTag(), workSource, request, new IRttCallback.Stub() { 220 @Override 221 public void onRangingFailure(int status) throws RemoteException { 222 clearCallingIdentity(); 223 executor.execute(() -> callback.onRangingFailure(status)); 224 } 225 226 @Override 227 public void onRangingResults(List<RangingResult> results) 228 throws RemoteException { 229 clearCallingIdentity(); 230 executor.execute(() -> callback.onRangingResults(results)); 231 } 232 }, extras); 233 } catch (RemoteException e) { 234 throw e.rethrowFromSystemServer(); 235 } 236 } 237 238 /** 239 * Cancel all ranging requests for the specified work sources. The requests have been requested 240 * using {@link #startRanging(WorkSource, RangingRequest, Executor, RangingResultCallback)}. 241 * 242 * @param workSource The work-sources of the requesters. 243 * 244 * @hide 245 */ 246 @SystemApi 247 @RequiresPermission(allOf = {LOCATION_HARDWARE}) cancelRanging(@ullable WorkSource workSource)248 public void cancelRanging(@Nullable WorkSource workSource) { 249 if (VDBG) { 250 Log.v(TAG, "cancelRanging: workSource=" + workSource); 251 } 252 253 try { 254 mService.cancelRanging(workSource); 255 } catch (RemoteException e) { 256 throw e.rethrowFromSystemServer(); 257 } 258 } 259 260 /** 261 * Returns a Bundle which represents the characteristics of the Wi-Fi RTT interface: a set of 262 * parameters which specify feature support. Each parameter can be accessed by the specified 263 * Bundle key, one of the {@code CHARACTERISTICS_KEY_*} value. 264 * <p> 265 * May return an empty Bundle if the Wi-Fi RTT service is not initialized. 266 * 267 * @return A Bundle specifying feature support of RTT. 268 */ 269 @RequiresPermission(ACCESS_WIFI_STATE) 270 @NonNull getRttCharacteristics()271 public Bundle getRttCharacteristics() { 272 try { 273 return mService.getRttCharacteristics(); 274 } catch (RemoteException e) { 275 throw e.rethrowFromSystemServer(); 276 } 277 } 278 } 279