1/*
2 * Copyright (C) 2017 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.net.NetworkSpecifier;
20import android.os.Parcel;
21import android.os.Parcelable;
22import android.util.Log;
23
24import libcore.util.HexEncoding;
25
26import java.security.MessageDigest;
27import java.security.NoSuchAlgorithmException;
28import java.util.Arrays;
29import java.util.HashSet;
30import java.util.Set;
31import java.util.StringJoiner;
32
33/**
34 * A network specifier object used to represent the capabilities of an network agent. A collection
35 * of multiple WifiAwareNetworkSpecifier objects whose matching critiera (satisfiedBy) is an OR:
36 * a match on any of the network specifiers in the collection is a match.
37 *
38 * This class is not intended for use in network requests.
39 *
40 * @hide
41 */
42public class WifiAwareAgentNetworkSpecifier extends NetworkSpecifier implements Parcelable {
43    private static final String TAG = "WifiAwareAgentNs";
44
45    private static final boolean VDBG = false; // STOPSHIP if true
46
47    private Set<ByteArrayWrapper> mNetworkSpecifiers = new HashSet<>();
48    private MessageDigest mDigester;
49
50    public WifiAwareAgentNetworkSpecifier() {
51        // do nothing, already initialized to empty
52    }
53
54    public WifiAwareAgentNetworkSpecifier(WifiAwareNetworkSpecifier ns) {
55        initialize();
56        mNetworkSpecifiers.add(convert(ns));
57    }
58
59    public WifiAwareAgentNetworkSpecifier(WifiAwareNetworkSpecifier[] nss) {
60        initialize();
61        for (WifiAwareNetworkSpecifier ns : nss) {
62            mNetworkSpecifiers.add(convert(ns));
63        }
64    }
65
66    public boolean isEmpty() {
67        return mNetworkSpecifiers.isEmpty();
68    }
69
70    @Override
71    public int describeContents() {
72        return 0;
73    }
74
75    @Override
76    public void writeToParcel(Parcel dest, int flags) {
77        dest.writeArray(mNetworkSpecifiers.toArray());
78    }
79
80    public static final Creator<WifiAwareAgentNetworkSpecifier> CREATOR =
81            new Creator<WifiAwareAgentNetworkSpecifier>() {
82                @Override
83                public WifiAwareAgentNetworkSpecifier createFromParcel(Parcel in) {
84                    WifiAwareAgentNetworkSpecifier agentNs = new WifiAwareAgentNetworkSpecifier();
85                    Object[] objs = in.readArray(null);
86                    for (Object obj : objs) {
87                        agentNs.mNetworkSpecifiers.add((ByteArrayWrapper) obj);
88                    }
89                    return agentNs;
90                }
91
92                @Override
93                public WifiAwareAgentNetworkSpecifier[] newArray(int size) {
94                    return new WifiAwareAgentNetworkSpecifier[size];
95                }
96            };
97
98    @Override
99    public int hashCode() {
100        return mNetworkSpecifiers.hashCode();
101    }
102
103    @Override
104    public boolean equals(Object obj) {
105        if (obj == this) {
106            return true;
107        }
108        if (!(obj instanceof WifiAwareAgentNetworkSpecifier)) {
109            return false;
110        }
111        return mNetworkSpecifiers.equals(((WifiAwareAgentNetworkSpecifier) obj).mNetworkSpecifiers);
112    }
113
114    @Override
115    public String toString() {
116        StringJoiner sj = new StringJoiner(",");
117        for (ByteArrayWrapper baw: mNetworkSpecifiers) {
118            sj.add(baw.toString());
119        }
120        return sj.toString();
121    }
122
123    @Override
124    public boolean satisfiedBy(NetworkSpecifier other) {
125        if (!(other instanceof WifiAwareAgentNetworkSpecifier)) {
126            return false;
127        }
128        WifiAwareAgentNetworkSpecifier otherNs = (WifiAwareAgentNetworkSpecifier) other;
129
130        // called as old.satifiedBy(new): satisfied if old contained in new
131        for (ByteArrayWrapper baw: mNetworkSpecifiers) {
132            if (!otherNs.mNetworkSpecifiers.contains(baw)) {
133                return false;
134            }
135        }
136
137        return true;
138    }
139
140    public boolean satisfiesAwareNetworkSpecifier(WifiAwareNetworkSpecifier ns) {
141        if (VDBG) Log.v(TAG, "satisfiesAwareNetworkSpecifier: ns=" + ns);
142        ByteArrayWrapper nsBytes = convert(ns);
143        return mNetworkSpecifiers.contains(nsBytes);
144    }
145
146    @Override
147    public void assertValidFromUid(int requestorUid) {
148        throw new SecurityException(
149                "WifiAwareAgentNetworkSpecifier should not be used in network requests");
150    }
151
152    private void initialize() {
153        try {
154            mDigester = MessageDigest.getInstance("SHA-256");
155        } catch (NoSuchAlgorithmException e) {
156            Log.e(TAG, "Can not instantiate a SHA-256 digester!? Will match nothing.");
157            return;
158        }
159    }
160
161    private ByteArrayWrapper convert(WifiAwareNetworkSpecifier ns) {
162        if (mDigester == null) {
163            return null;
164        }
165
166        Parcel parcel = Parcel.obtain();
167        ns.writeToParcel(parcel, 0);
168        byte[] bytes = parcel.marshall();
169
170        mDigester.reset();
171        mDigester.update(bytes);
172        return new ByteArrayWrapper(mDigester.digest());
173    }
174
175    private static class ByteArrayWrapper implements Parcelable {
176        private byte[] mData;
177
178        ByteArrayWrapper(byte[] data) {
179            mData = data;
180        }
181
182        @Override
183        public int hashCode() {
184            return Arrays.hashCode(mData);
185        }
186
187        @Override
188        public boolean equals(Object obj) {
189            if (obj == this) {
190                return true;
191            }
192            if (!(obj instanceof ByteArrayWrapper)) {
193                return false;
194            }
195            return Arrays.equals(((ByteArrayWrapper) obj).mData, mData);
196        }
197
198        @Override
199        public int describeContents() {
200            return 0;
201        }
202
203        @Override
204        public void writeToParcel(Parcel dest, int flags) {
205            dest.writeBlob(mData);
206        }
207
208        public static final Creator<ByteArrayWrapper> CREATOR =
209                new Creator<ByteArrayWrapper>() {
210                    @Override
211                    public ByteArrayWrapper createFromParcel(Parcel in) {
212                        return new ByteArrayWrapper(in.readBlob());
213                    }
214
215                    @Override
216                    public ByteArrayWrapper[] newArray(int size) {
217                        return new ByteArrayWrapper[size];
218                    }
219                };
220
221        @Override
222        public String toString() {
223            return new String(HexEncoding.encode(mData));
224        }
225    }
226}
227