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 24 import android.annotation.CallbackExecutor; 25 import android.annotation.NonNull; 26 import android.annotation.Nullable; 27 import android.annotation.RequiresPermission; 28 import android.annotation.SdkConstant; 29 import android.annotation.SystemApi; 30 import android.annotation.SystemService; 31 import android.content.Context; 32 import android.os.Binder; 33 import android.os.RemoteException; 34 import android.os.WorkSource; 35 import android.util.Log; 36 37 import java.util.List; 38 import java.util.concurrent.Executor; 39 40 /** 41 * This class provides the primary API for measuring distance (range) to other devices using the 42 * IEEE 802.11mc Wi-Fi Round Trip Time (RTT) technology. 43 * <p> 44 * The devices which can be ranged include: 45 * <li>Access Points (APs) 46 * <li>Wi-Fi Aware peers 47 * <p> 48 * Ranging requests are triggered using 49 * {@link #startRanging(RangingRequest, Executor, RangingResultCallback)}. Results (in case of 50 * successful operation) are returned in the {@link RangingResultCallback#onRangingResults(List)} 51 * callback. 52 * <p> 53 * Wi-Fi RTT may not be usable at some points, e.g. when Wi-Fi is disabled. To validate that 54 * the functionality is available use the {@link #isAvailable()} function. To track 55 * changes in RTT usability register for the {@link #ACTION_WIFI_RTT_STATE_CHANGED} 56 * broadcast. Note that this broadcast is not sticky - you should register for it and then 57 * check the above API to avoid a race condition. 58 */ 59 @SystemService(Context.WIFI_RTT_RANGING_SERVICE) 60 public class WifiRttManager { 61 private static final String TAG = "WifiRttManager"; 62 private static final boolean VDBG = false; 63 64 private final Context mContext; 65 private final IWifiRttManager mService; 66 67 /** 68 * Broadcast intent action to indicate that the state of Wi-Fi RTT availability has changed. 69 * Use the {@link #isAvailable()} to query the current status. 70 * This broadcast is <b>not</b> sticky, use the {@link #isAvailable()} API after registering 71 * the broadcast to check the current state of Wi-Fi RTT. 72 * <p>Note: The broadcast is only delivered to registered receivers - no manifest registered 73 * components will be launched. 74 */ 75 @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) 76 public static final String ACTION_WIFI_RTT_STATE_CHANGED = 77 "android.net.wifi.rtt.action.WIFI_RTT_STATE_CHANGED"; 78 79 /** @hide */ WifiRttManager(Context context, IWifiRttManager service)80 public WifiRttManager(Context context, IWifiRttManager service) { 81 mContext = context; 82 mService = service; 83 } 84 85 /** 86 * Returns the current status of RTT API: whether or not RTT is available. To track 87 * changes in the state of RTT API register for the 88 * {@link #ACTION_WIFI_RTT_STATE_CHANGED} broadcast. 89 * <p>Note: availability of RTT does not mean that the app can use the API. The app's 90 * permissions and platform Location Mode are validated at run-time. 91 * 92 * @return A boolean indicating whether the app can use the RTT API at this time (true) or 93 * not (false). 94 */ isAvailable()95 public boolean isAvailable() { 96 try { 97 return mService.isAvailable(); 98 } catch (RemoteException e) { 99 throw e.rethrowFromSystemServer(); 100 } 101 } 102 103 /** 104 * Initiate a request to range to a set of devices specified in the {@link RangingRequest}. 105 * Results will be returned in the {@link RangingResultCallback} set of callbacks. 106 * 107 * @param request A request specifying a set of devices whose distance measurements are 108 * requested. 109 * @param executor The Executor on which to run the callback. 110 * @param callback A callback for the result of the ranging request. 111 */ 112 @RequiresPermission(allOf = {ACCESS_FINE_LOCATION, CHANGE_WIFI_STATE, ACCESS_WIFI_STATE}) startRanging(@onNull RangingRequest request, @NonNull @CallbackExecutor Executor executor, @NonNull RangingResultCallback callback)113 public void startRanging(@NonNull RangingRequest request, 114 @NonNull @CallbackExecutor Executor executor, @NonNull RangingResultCallback callback) { 115 startRanging(null, request, executor, callback); 116 } 117 118 /** 119 * Initiate a request to range to a set of devices specified in the {@link RangingRequest}. 120 * Results will be returned in the {@link RangingResultCallback} set of callbacks. 121 * 122 * @param workSource A mechanism to specify an alternative work-source for the request. 123 * @param request A request specifying a set of devices whose distance measurements are 124 * requested. 125 * @param executor The Executor on which to run the callback. 126 * @param callback A callback for the result of the ranging request. 127 * 128 * @hide 129 */ 130 @SystemApi 131 @RequiresPermission(allOf = {LOCATION_HARDWARE, ACCESS_FINE_LOCATION, CHANGE_WIFI_STATE, 132 ACCESS_WIFI_STATE}) startRanging(@ullable WorkSource workSource, @NonNull RangingRequest request, @NonNull @CallbackExecutor Executor executor, @NonNull RangingResultCallback callback)133 public void startRanging(@Nullable WorkSource workSource, @NonNull RangingRequest request, 134 @NonNull @CallbackExecutor Executor executor, @NonNull RangingResultCallback callback) { 135 if (VDBG) { 136 Log.v(TAG, "startRanging: workSource=" + workSource + ", request=" + request 137 + ", callback=" + callback + ", executor=" + executor); 138 } 139 140 if (executor == null) { 141 throw new IllegalArgumentException("Null executor provided"); 142 } 143 if (callback == null) { 144 throw new IllegalArgumentException("Null callback provided"); 145 } 146 147 Binder binder = new Binder(); 148 try { 149 mService.startRanging(binder, mContext.getOpPackageName(), workSource, request, 150 new IRttCallback.Stub() { 151 @Override 152 public void onRangingFailure(int status) throws RemoteException { 153 clearCallingIdentity(); 154 executor.execute(() -> callback.onRangingFailure(status)); 155 } 156 157 @Override 158 public void onRangingResults(List<RangingResult> results) 159 throws RemoteException { 160 clearCallingIdentity(); 161 executor.execute(() -> callback.onRangingResults(results)); 162 } 163 }); 164 } catch (RemoteException e) { 165 throw e.rethrowFromSystemServer(); 166 } 167 } 168 169 /** 170 * Cancel all ranging requests for the specified work sources. The requests have been requested 171 * using {@link #startRanging(WorkSource, RangingRequest, Executor, RangingResultCallback)}. 172 * 173 * @param workSource The work-sources of the requesters. 174 * 175 * @hide 176 */ 177 @SystemApi 178 @RequiresPermission(allOf = {LOCATION_HARDWARE}) cancelRanging(@ullable WorkSource workSource)179 public void cancelRanging(@Nullable WorkSource workSource) { 180 if (VDBG) { 181 Log.v(TAG, "cancelRanging: workSource=" + workSource); 182 } 183 184 try { 185 mService.cancelRanging(workSource); 186 } catch (RemoteException e) { 187 throw e.rethrowFromSystemServer(); 188 } 189 } 190 } 191