1/*
2 * Copyright (C) 2016 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
17package android.net.wifi.aware;
18
19import android.os.Parcel;
20import android.os.Parcelable;
21
22import java.util.Arrays;
23
24/**
25 * Defines a request object to configure a Wi-Fi Aware network. Built using
26 * {@link ConfigRequest.Builder}. Configuration is requested using
27 * {@link WifiAwareManager#attach(AttachCallback, android.os.Handler)}.
28 * Note that the actual achieved configuration may be different from the
29 * requested configuration - since different applications may request different
30 * configurations.
31 *
32 * @hide
33 */
34public final class ConfigRequest implements Parcelable {
35    /**
36     * Lower range of possible cluster ID.
37     */
38    public static final int CLUSTER_ID_MIN = 0;
39
40    /**
41     * Upper range of possible cluster ID.
42     */
43    public static final int CLUSTER_ID_MAX = 0xFFFF;
44
45    /**
46     * Indices for configuration variables which are specified per band.
47     */
48    public static final int NAN_BAND_24GHZ = 0;
49    public static final int NAN_BAND_5GHZ = 1;
50
51    /**
52     * Magic values for Discovery Window (DW) interval configuration
53     */
54    public static final int DW_INTERVAL_NOT_INIT = -1;
55    public static final int DW_DISABLE = 0; // only valid for 5GHz
56
57    /**
58     * Indicates whether 5G band support is requested.
59     */
60    public final boolean mSupport5gBand;
61
62    /**
63     * Specifies the desired master preference.
64     */
65    public final int mMasterPreference;
66
67    /**
68     * Specifies the desired lower range of the cluster ID. Must be lower then
69     * {@link ConfigRequest#mClusterHigh}.
70     */
71    public final int mClusterLow;
72
73    /**
74     * Specifies the desired higher range of the cluster ID. Must be higher then
75     * {@link ConfigRequest#mClusterLow}.
76     */
77    public final int mClusterHigh;
78
79    /**
80     * Specifies the discovery window interval for the device on NAN_BAND_*.
81     */
82    public final int mDiscoveryWindowInterval[];
83
84    private ConfigRequest(boolean support5gBand, int masterPreference, int clusterLow,
85            int clusterHigh, int discoveryWindowInterval[]) {
86        mSupport5gBand = support5gBand;
87        mMasterPreference = masterPreference;
88        mClusterLow = clusterLow;
89        mClusterHigh = clusterHigh;
90        mDiscoveryWindowInterval = discoveryWindowInterval;
91    }
92
93    @Override
94    public String toString() {
95        return "ConfigRequest [mSupport5gBand=" + mSupport5gBand + ", mMasterPreference="
96                + mMasterPreference + ", mClusterLow=" + mClusterLow + ", mClusterHigh="
97                + mClusterHigh + ", mDiscoveryWindowInterval="
98                + Arrays.toString(mDiscoveryWindowInterval) + "]";
99    }
100
101    @Override
102    public int describeContents() {
103        return 0;
104    }
105
106    @Override
107    public void writeToParcel(Parcel dest, int flags) {
108        dest.writeInt(mSupport5gBand ? 1 : 0);
109        dest.writeInt(mMasterPreference);
110        dest.writeInt(mClusterLow);
111        dest.writeInt(mClusterHigh);
112        dest.writeIntArray(mDiscoveryWindowInterval);
113    }
114
115    public static final Creator<ConfigRequest> CREATOR = new Creator<ConfigRequest>() {
116        @Override
117        public ConfigRequest[] newArray(int size) {
118            return new ConfigRequest[size];
119        }
120
121        @Override
122        public ConfigRequest createFromParcel(Parcel in) {
123            boolean support5gBand = in.readInt() != 0;
124            int masterPreference = in.readInt();
125            int clusterLow = in.readInt();
126            int clusterHigh = in.readInt();
127            int discoveryWindowInterval[] = in.createIntArray();
128
129            return new ConfigRequest(support5gBand, masterPreference, clusterLow, clusterHigh,
130                    discoveryWindowInterval);
131        }
132    };
133
134    @Override
135    public boolean equals(Object o) {
136        if (this == o) {
137            return true;
138        }
139
140        if (!(o instanceof ConfigRequest)) {
141            return false;
142        }
143
144        ConfigRequest lhs = (ConfigRequest) o;
145
146        return mSupport5gBand == lhs.mSupport5gBand && mMasterPreference == lhs.mMasterPreference
147                && mClusterLow == lhs.mClusterLow && mClusterHigh == lhs.mClusterHigh
148                && Arrays.equals(mDiscoveryWindowInterval, lhs.mDiscoveryWindowInterval);
149    }
150
151    @Override
152    public int hashCode() {
153        int result = 17;
154
155        result = 31 * result + (mSupport5gBand ? 1 : 0);
156        result = 31 * result + mMasterPreference;
157        result = 31 * result + mClusterLow;
158        result = 31 * result + mClusterHigh;
159        result = 31 * result + Arrays.hashCode(mDiscoveryWindowInterval);
160
161        return result;
162    }
163
164    /**
165     * Verifies that the contents of the ConfigRequest are valid. Otherwise
166     * throws an IllegalArgumentException.
167     */
168    public void validate() throws IllegalArgumentException {
169        if (mMasterPreference < 0) {
170            throw new IllegalArgumentException(
171                    "Master Preference specification must be non-negative");
172        }
173        if (mMasterPreference == 1 || mMasterPreference == 255 || mMasterPreference > 255) {
174            throw new IllegalArgumentException("Master Preference specification must not "
175                    + "exceed 255 or use 1 or 255 (reserved values)");
176        }
177        if (mClusterLow < CLUSTER_ID_MIN) {
178            throw new IllegalArgumentException("Cluster specification must be non-negative");
179        }
180        if (mClusterLow > CLUSTER_ID_MAX) {
181            throw new IllegalArgumentException("Cluster specification must not exceed 0xFFFF");
182        }
183        if (mClusterHigh < CLUSTER_ID_MIN) {
184            throw new IllegalArgumentException("Cluster specification must be non-negative");
185        }
186        if (mClusterHigh > CLUSTER_ID_MAX) {
187            throw new IllegalArgumentException("Cluster specification must not exceed 0xFFFF");
188        }
189        if (mClusterLow > mClusterHigh) {
190            throw new IllegalArgumentException(
191                    "Invalid argument combination - must have Cluster Low <= Cluster High");
192        }
193        if (mDiscoveryWindowInterval.length != 2) {
194            throw new IllegalArgumentException(
195                    "Invalid discovery window interval: must have 2 elements (2.4 & 5");
196        }
197        if (mDiscoveryWindowInterval[NAN_BAND_24GHZ] != DW_INTERVAL_NOT_INIT &&
198                (mDiscoveryWindowInterval[NAN_BAND_24GHZ] < 1 // valid for 2.4GHz: [1-5]
199                || mDiscoveryWindowInterval[NAN_BAND_24GHZ] > 5)) {
200            throw new IllegalArgumentException(
201                    "Invalid discovery window interval for 2.4GHz: valid is UNSET or [1,5]");
202        }
203        if (mDiscoveryWindowInterval[NAN_BAND_5GHZ] != DW_INTERVAL_NOT_INIT &&
204                (mDiscoveryWindowInterval[NAN_BAND_5GHZ] < 0 // valid for 5GHz: [0-5]
205                || mDiscoveryWindowInterval[NAN_BAND_5GHZ] > 5)) {
206            throw new IllegalArgumentException(
207                "Invalid discovery window interval for 5GHz: valid is UNSET or [0,5]");
208        }
209
210    }
211
212    /**
213     * Builder used to build {@link ConfigRequest} objects.
214     */
215    public static final class Builder {
216        private boolean mSupport5gBand = false;
217        private int mMasterPreference = 0;
218        private int mClusterLow = CLUSTER_ID_MIN;
219        private int mClusterHigh = CLUSTER_ID_MAX;
220        private int mDiscoveryWindowInterval[] = {DW_INTERVAL_NOT_INIT, DW_INTERVAL_NOT_INIT};
221
222        /**
223         * Specify whether 5G band support is required in this request. Disabled by default.
224         *
225         * @param support5gBand Support for 5G band is required.
226         *
227         * @return The builder to facilitate chaining
228         *         {@code builder.setXXX(..).setXXX(..)}.
229         */
230        public Builder setSupport5gBand(boolean support5gBand) {
231            mSupport5gBand = support5gBand;
232            return this;
233        }
234
235        /**
236         * Specify the Master Preference requested. The permitted range is 0 (the default) to
237         * 255 with 1 and 255 excluded (reserved).
238         *
239         * @param masterPreference The requested master preference
240         *
241         * @return The builder to facilitate chaining
242         *         {@code builder.setXXX(..).setXXX(..)}.
243         */
244        public Builder setMasterPreference(int masterPreference) {
245            if (masterPreference < 0) {
246                throw new IllegalArgumentException(
247                        "Master Preference specification must be non-negative");
248            }
249            if (masterPreference == 1 || masterPreference == 255 || masterPreference > 255) {
250                throw new IllegalArgumentException("Master Preference specification must not "
251                        + "exceed 255 or use 1 or 255 (reserved values)");
252            }
253
254            mMasterPreference = masterPreference;
255            return this;
256        }
257
258        /**
259         * The Cluster ID is generated randomly for new Aware networks. Specify
260         * the lower range of the cluster ID. The upper range is specified using
261         * the {@link ConfigRequest.Builder#setClusterHigh(int)}. The permitted
262         * range is 0 (the default) to the value specified by
263         * {@link ConfigRequest.Builder#setClusterHigh(int)}. Equality of Low and High is
264         * permitted which restricts the Cluster ID to the specified value.
265         *
266         * @param clusterLow The lower range of the generated cluster ID.
267         *
268         * @return The builder to facilitate chaining
269         *         {@code builder.setClusterLow(..).setClusterHigh(..)}.
270         */
271        public Builder setClusterLow(int clusterLow) {
272            if (clusterLow < CLUSTER_ID_MIN) {
273                throw new IllegalArgumentException("Cluster specification must be non-negative");
274            }
275            if (clusterLow > CLUSTER_ID_MAX) {
276                throw new IllegalArgumentException("Cluster specification must not exceed 0xFFFF");
277            }
278
279            mClusterLow = clusterLow;
280            return this;
281        }
282
283        /**
284         * The Cluster ID is generated randomly for new Aware networks. Specify
285         * the lower upper of the cluster ID. The lower range is specified using
286         * the {@link ConfigRequest.Builder#setClusterLow(int)}. The permitted
287         * range is the value specified by
288         * {@link ConfigRequest.Builder#setClusterLow(int)} to 0xFFFF (the default). Equality of
289         * Low and High is permitted which restricts the Cluster ID to the specified value.
290         *
291         * @param clusterHigh The upper range of the generated cluster ID.
292         *
293         * @return The builder to facilitate chaining
294         *         {@code builder.setClusterLow(..).setClusterHigh(..)}.
295         */
296        public Builder setClusterHigh(int clusterHigh) {
297            if (clusterHigh < CLUSTER_ID_MIN) {
298                throw new IllegalArgumentException("Cluster specification must be non-negative");
299            }
300            if (clusterHigh > CLUSTER_ID_MAX) {
301                throw new IllegalArgumentException("Cluster specification must not exceed 0xFFFF");
302            }
303
304            mClusterHigh = clusterHigh;
305            return this;
306        }
307
308        /**
309         * The discovery window interval specifies the discovery windows in which the device will be
310         * awake. The configuration enables trading off latency vs. power (higher interval means
311         * higher discovery latency but lower power).
312         *
313         * @param band Either {@link #NAN_BAND_24GHZ} or {@link #NAN_BAND_5GHZ}.
314         * @param interval A value of 1, 2, 3, 4, or 5 indicating an interval of 2^(interval-1). For
315         *                 the 5GHz band a value of 0 indicates that the device will not be awake
316         *                 for any discovery windows.
317         *
318         * @return The builder itself to facilitate chaining operations
319         *         {@code builder.setDiscoveryWindowInterval(...).setMasterPreference(...)}.
320         */
321        public Builder setDiscoveryWindowInterval(int band, int interval) {
322            if (band != NAN_BAND_24GHZ && band != NAN_BAND_5GHZ) {
323                throw new IllegalArgumentException("Invalid band value");
324            }
325            if ((band == NAN_BAND_24GHZ && (interval < 1 || interval > 5))
326                    || (band == NAN_BAND_5GHZ && (interval < 0 || interval > 5))) {
327                throw new IllegalArgumentException(
328                        "Invalid interval value: 2.4 GHz [1,5] or 5GHz [0,5]");
329            }
330
331            mDiscoveryWindowInterval[band] = interval;
332            return this;
333        }
334
335        /**
336         * Build {@link ConfigRequest} given the current requests made on the
337         * builder.
338         */
339        public ConfigRequest build() {
340            if (mClusterLow > mClusterHigh) {
341                throw new IllegalArgumentException(
342                        "Invalid argument combination - must have Cluster Low <= Cluster High");
343            }
344
345            return new ConfigRequest(mSupport5gBand, mMasterPreference, mClusterLow, mClusterHigh,
346                    mDiscoveryWindowInterval);
347        }
348    }
349}
350