summaryrefslogtreecommitdiff
path: root/android/net/wifi/rtt/WifiRttManager.java
blob: b4c690f4840d349e3b2d558a1aa3a44b1f8d906e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
package android.net.wifi.rtt;

import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
import static android.Manifest.permission.ACCESS_WIFI_STATE;
import static android.Manifest.permission.CHANGE_WIFI_STATE;
import static android.Manifest.permission.LOCATION_HARDWARE;

import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
import android.os.Binder;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.os.WorkSource;
import android.util.Log;

import java.util.List;

/**
 * This class provides the primary API for measuring distance (range) to other devices using the
 * IEEE 802.11mc Wi-Fi Round Trip Time (RTT) technology.
 * <p>
 * The devices which can be ranged include:
 * <li>Access Points (APs)
 * <li>Wi-Fi Aware peers
 * <p>
 * Ranging requests are triggered using
 * {@link #startRanging(RangingRequest, RangingResultCallback, Handler)}. Results (in case of
 * successful operation) are returned in the {@link RangingResultCallback#onRangingResults(List)}
 * callback.
 * <p>
 *     Wi-Fi RTT may not be usable at some points, e.g. when Wi-Fi is disabled. To validate that
 *     the functionality is available use the {@link #isAvailable()} function. To track
 *     changes in RTT usability register for the {@link #ACTION_WIFI_RTT_STATE_CHANGED}
 *     broadcast. Note that this broadcast is not sticky - you should register for it and then
 *     check the above API to avoid a race condition.
 *
 * @hide RTT_API
 */
@SystemService(Context.WIFI_RTT_RANGING_SERVICE)
public class WifiRttManager {
    private static final String TAG = "WifiRttManager";
    private static final boolean VDBG = false;

    private final Context mContext;
    private final IWifiRttManager mService;

    /**
     * Broadcast intent action to indicate that the state of Wi-Fi RTT availability has changed.
     * Use the {@link #isAvailable()} to query the current status.
     * This broadcast is <b>not</b> sticky, use the {@link #isAvailable()} API after registering
     * the broadcast to check the current state of Wi-Fi RTT.
     * <p>Note: The broadcast is only delivered to registered receivers - no manifest registered
     * components will be launched.
     */
    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
    public static final String ACTION_WIFI_RTT_STATE_CHANGED =
            "android.net.wifi.rtt.action.WIFI_RTT_STATE_CHANGED";

    /** @hide */
    public WifiRttManager(Context context, IWifiRttManager service) {
        mContext = context;
        mService = service;
    }

    /**
     * Returns the current status of RTT API: whether or not RTT is available. To track
     * changes in the state of RTT API register for the
     * {@link #ACTION_WIFI_RTT_STATE_CHANGED} broadcast.
     *
     * @return A boolean indicating whether the app can use the RTT API at this time (true) or
     * not (false).
     */
    public boolean isAvailable() {
        try {
            return mService.isAvailable();
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Initiate a request to range to a set of devices specified in the {@link RangingRequest}.
     * Results will be returned in the {@link RangingResultCallback} set of callbacks.
     *
     * @param request  A request specifying a set of devices whose distance measurements are
     *                 requested.
     * @param callback A callback for the result of the ranging request.
     * @param handler  The Handler on whose thread to execute the callbacks of the {@code
     *                 callback} object. If a null is provided then the application's main thread
     *                 will be used.
     */
    @RequiresPermission(allOf = {ACCESS_COARSE_LOCATION, CHANGE_WIFI_STATE, ACCESS_WIFI_STATE})
    public void startRanging(RangingRequest request, RangingResultCallback callback,
            @Nullable Handler handler) {
        startRanging(null, request, callback, handler);
    }

    /**
     * Initiate a request to range to a set of devices specified in the {@link RangingRequest}.
     * Results will be returned in the {@link RangingResultCallback} set of callbacks.
     *
     * @param workSource A mechanism to specify an alternative work-source for the request.
     * @param request  A request specifying a set of devices whose distance measurements are
     *                 requested.
     * @param callback A callback for the result of the ranging request.
     * @param handler  The Handler on whose thread to execute the callbacks of the {@code
     *                 callback} object. If a null is provided then the application's main thread
     *                 will be used.
     *
     * @hide (@SystemApi)
     */
    @RequiresPermission(allOf = {LOCATION_HARDWARE, ACCESS_COARSE_LOCATION, CHANGE_WIFI_STATE,
            ACCESS_WIFI_STATE})
    public void startRanging(@Nullable WorkSource workSource, RangingRequest request,
            RangingResultCallback callback, @Nullable Handler handler) {
        if (VDBG) {
            Log.v(TAG, "startRanging: workSource=" + workSource + ", request=" + request
                    + ", callback=" + callback + ", handler=" + handler);
        }

        Looper looper = (handler == null) ? Looper.getMainLooper() : handler.getLooper();
        Binder binder = new Binder();
        try {
            mService.startRanging(binder, mContext.getOpPackageName(), workSource, request,
                    new RttCallbackProxy(looper, callback));
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Cancel all ranging requests for the specified work sources. The requests have been requested
     * using {@link #startRanging(WorkSource, RangingRequest, RangingResultCallback, Handler)}.
     *
     * @param workSource The work-sources of the requesters.
     *
     * @hide (@SystemApi)
     */
    @RequiresPermission(allOf = {LOCATION_HARDWARE})
    public void cancelRanging(WorkSource workSource) {
        if (VDBG) {
            Log.v(TAG, "cancelRanging: workSource=" + workSource);
        }

        try {
            mService.cancelRanging(workSource);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    private static class RttCallbackProxy extends IRttCallback.Stub {
        private final Handler mHandler;
        private final RangingResultCallback mCallback;

        RttCallbackProxy(Looper looper, RangingResultCallback callback) {
            mHandler = new Handler(looper);
            mCallback = callback;
        }

        @Override
        public void onRangingFailure(int status) throws RemoteException {
            if (VDBG) Log.v(TAG, "RttCallbackProxy: onRangingFailure: status=" + status);
            mHandler.post(() -> {
               mCallback.onRangingFailure(status);
            });
        }

        @Override
        public void onRangingResults(List<RangingResult> results) throws RemoteException {
            if (VDBG) Log.v(TAG, "RttCallbackProxy: onRanginResults: results=" + results);
            mHandler.post(() -> {
               mCallback.onRangingResults(results);
            });
        }
    }
}