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