blob: 98f356722bf373d922bda8ee6925c2e1036d54fd [file] [log] [blame]
Hugo Benichi59c8e422017-10-12 21:33:40 +09001/*
2 * Copyright 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;
18
Hugo Benichi84bb7fc2017-11-16 14:40:16 +090019import android.annotation.IntDef;
Hugo Benichi48872c62018-01-12 09:46:29 +090020import android.annotation.NonNull;
Mathew Inwoodfa3a7462018-08-08 14:52:47 +010021import android.annotation.UnsupportedAppUsage;
Hugo Benichiac52e402017-11-09 00:22:25 +090022import android.os.Parcel;
23import android.os.Parcelable;
24
25import com.android.internal.util.BitUtils;
Hugo Benichi84bb7fc2017-11-16 14:40:16 +090026import com.android.internal.util.Preconditions;
Hugo Benichi59c8e422017-10-12 21:33:40 +090027
Hugo Benichi84bb7fc2017-11-16 14:40:16 +090028import java.lang.annotation.Retention;
29import java.lang.annotation.RetentionPolicy;
Jong Wook Kimf0a55cc2018-01-31 19:03:19 -080030import java.security.SecureRandom;
Hugo Benichi59c8e422017-10-12 21:33:40 +090031import java.util.Arrays;
Hugo Benichiac52e402017-11-09 00:22:25 +090032import java.util.Random;
Hugo Benichi59c8e422017-10-12 21:33:40 +090033
34/**
Hugo Benichi84bb7fc2017-11-16 14:40:16 +090035 * Representation of a MAC address.
36 *
37 * This class only supports 48 bits long addresses and does not support 64 bits long addresses.
38 * Instances of this class are immutable.
Hugo Benichi59c8e422017-10-12 21:33:40 +090039 */
Hugo Benichiac52e402017-11-09 00:22:25 +090040public final class MacAddress implements Parcelable {
Hugo Benichi59c8e422017-10-12 21:33:40 +090041
42 private static final int ETHER_ADDR_LEN = 6;
Hugo Benichiac52e402017-11-09 00:22:25 +090043 private static final byte[] ETHER_ADDR_BROADCAST = addr(0xff, 0xff, 0xff, 0xff, 0xff, 0xff);
Hugo Benichi59c8e422017-10-12 21:33:40 +090044
Hugo Benichi84bb7fc2017-11-16 14:40:16 +090045 /**
46 * The MacAddress representing the unique broadcast MAC address.
47 */
48 public static final MacAddress BROADCAST_ADDRESS = MacAddress.fromBytes(ETHER_ADDR_BROADCAST);
Hugo Benichiac52e402017-11-09 00:22:25 +090049
Hugo Benichi84bb7fc2017-11-16 14:40:16 +090050 /**
51 * The MacAddress zero MAC address.
52 * @hide
53 */
Mathew Inwoodfa3a7462018-08-08 14:52:47 +010054 @UnsupportedAppUsage
Hugo Benichiac52e402017-11-09 00:22:25 +090055 public static final MacAddress ALL_ZEROS_ADDRESS = new MacAddress(0);
56
Hugo Benichi84bb7fc2017-11-16 14:40:16 +090057 /** @hide */
58 @Retention(RetentionPolicy.SOURCE)
59 @IntDef(prefix = { "TYPE_" }, value = {
60 TYPE_UNKNOWN,
61 TYPE_UNICAST,
62 TYPE_MULTICAST,
63 TYPE_BROADCAST,
64 })
65 public @interface MacAddressType { }
Hugo Benichi59c8e422017-10-12 21:33:40 +090066
Hugo Benichi48872c62018-01-12 09:46:29 +090067 /** @hide Indicates a MAC address of unknown type. */
Hugo Benichi84bb7fc2017-11-16 14:40:16 +090068 public static final int TYPE_UNKNOWN = 0;
69 /** Indicates a MAC address is a unicast address. */
70 public static final int TYPE_UNICAST = 1;
71 /** Indicates a MAC address is a multicast address. */
72 public static final int TYPE_MULTICAST = 2;
73 /** Indicates a MAC address is the broadcast address. */
74 public static final int TYPE_BROADCAST = 3;
Hugo Benichiac52e402017-11-09 00:22:25 +090075
Hugo Benichi84bb7fc2017-11-16 14:40:16 +090076 private static final long VALID_LONG_MASK = (1L << 48) - 1;
77 private static final long LOCALLY_ASSIGNED_MASK = MacAddress.fromString("2:0:0:0:0:0").mAddr;
78 private static final long MULTICAST_MASK = MacAddress.fromString("1:0:0:0:0:0").mAddr;
79 private static final long OUI_MASK = MacAddress.fromString("ff:ff:ff:0:0:0").mAddr;
80 private static final long NIC_MASK = MacAddress.fromString("0:0:0:ff:ff:ff").mAddr;
81 private static final MacAddress BASE_GOOGLE_MAC = MacAddress.fromString("da:a1:19:0:0:0");
82
83 // Internal representation of the MAC address as a single 8 byte long.
Hugo Benichiac52e402017-11-09 00:22:25 +090084 // The encoding scheme sets the two most significant bytes to 0. The 6 bytes of the
Hugo Benichi84bb7fc2017-11-16 14:40:16 +090085 // MAC address are encoded in the 6 least significant bytes of the long, where the first
Hugo Benichiac52e402017-11-09 00:22:25 +090086 // byte of the array is mapped to the 3rd highest logical byte of the long, the second
87 // byte of the array is mapped to the 4th highest logical byte of the long, and so on.
88 private final long mAddr;
89
90 private MacAddress(long addr) {
Hugo Benichi84bb7fc2017-11-16 14:40:16 +090091 mAddr = (VALID_LONG_MASK & addr);
Hugo Benichiac52e402017-11-09 00:22:25 +090092 }
93
Hugo Benichi84bb7fc2017-11-16 14:40:16 +090094 /**
95 * Returns the type of this address.
96 *
97 * @return the int constant representing the MAC address type of this MacAddress.
98 */
Hugo Benichi48872c62018-01-12 09:46:29 +090099 public @MacAddressType int getAddressType() {
Hugo Benichiac52e402017-11-09 00:22:25 +0900100 if (equals(BROADCAST_ADDRESS)) {
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900101 return TYPE_BROADCAST;
Hugo Benichiac52e402017-11-09 00:22:25 +0900102 }
103 if (isMulticastAddress()) {
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900104 return TYPE_MULTICAST;
Hugo Benichiac52e402017-11-09 00:22:25 +0900105 }
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900106 return TYPE_UNICAST;
Hugo Benichiac52e402017-11-09 00:22:25 +0900107 }
108
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900109 /**
110 * @return true if this MacAddress is a multicast address.
111 * @hide
112 */
Hugo Benichiac52e402017-11-09 00:22:25 +0900113 public boolean isMulticastAddress() {
114 return (mAddr & MULTICAST_MASK) != 0;
115 }
116
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900117 /**
118 * @return true if this MacAddress is a locally assigned address.
119 */
Hugo Benichiac52e402017-11-09 00:22:25 +0900120 public boolean isLocallyAssigned() {
121 return (mAddr & LOCALLY_ASSIGNED_MASK) != 0;
122 }
123
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900124 /**
125 * @return a byte array representation of this MacAddress.
126 */
Hugo Benichi48872c62018-01-12 09:46:29 +0900127 public @NonNull byte[] toByteArray() {
Hugo Benichiac52e402017-11-09 00:22:25 +0900128 return byteAddrFromLongAddr(mAddr);
129 }
130
131 @Override
Hugo Benichi48872c62018-01-12 09:46:29 +0900132 public @NonNull String toString() {
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900133 return stringAddrFromLongAddr(mAddr);
134 }
135
136 /**
Hugo Benichia0ecf382017-12-15 10:07:35 +0900137 * @return a String representation of the OUI part of this MacAddress made of 3 hexadecimal
138 * numbers in [0,ff] joined by ':' characters.
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900139 */
Hugo Benichi48872c62018-01-12 09:46:29 +0900140 public @NonNull String toOuiString() {
Hugo Benichia0ecf382017-12-15 10:07:35 +0900141 return String.format(
142 "%02x:%02x:%02x", (mAddr >> 40) & 0xff, (mAddr >> 32) & 0xff, (mAddr >> 24) & 0xff);
Hugo Benichiac52e402017-11-09 00:22:25 +0900143 }
144
145 @Override
146 public int hashCode() {
147 return (int) ((mAddr >> 32) ^ mAddr);
148 }
149
150 @Override
151 public boolean equals(Object o) {
152 return (o instanceof MacAddress) && ((MacAddress) o).mAddr == mAddr;
153 }
154
155 @Override
156 public void writeToParcel(Parcel out, int flags) {
157 out.writeLong(mAddr);
158 }
159
160 @Override
161 public int describeContents() {
162 return 0;
163 }
164
165 public static final Parcelable.Creator<MacAddress> CREATOR =
166 new Parcelable.Creator<MacAddress>() {
167 public MacAddress createFromParcel(Parcel in) {
168 return new MacAddress(in.readLong());
169 }
170
171 public MacAddress[] newArray(int size) {
172 return new MacAddress[size];
173 }
174 };
175
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900176 /**
177 * Returns true if the given byte array is an valid MAC address.
178 * A valid byte array representation for a MacAddress is a non-null array of length 6.
179 *
180 * @param addr a byte array.
181 * @return true if the given byte array is not null and has the length of a MAC address.
182 *
183 * @hide
184 */
Hugo Benichi59c8e422017-10-12 21:33:40 +0900185 public static boolean isMacAddress(byte[] addr) {
186 return addr != null && addr.length == ETHER_ADDR_LEN;
187 }
188
189 /**
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900190 * Returns the MAC address type of the MAC address represented by the given byte array,
191 * or null if the given byte array does not represent a MAC address.
192 * A valid byte array representation for a MacAddress is a non-null array of length 6.
193 *
194 * @param addr a byte array representing a MAC address.
195 * @return the int constant representing the MAC address type of the MAC address represented
196 * by the given byte array, or type UNKNOWN if the byte array is not a valid MAC address.
197 *
198 * @hide
Hugo Benichiac52e402017-11-09 00:22:25 +0900199 */
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900200 public static int macAddressType(byte[] addr) {
Hugo Benichi59c8e422017-10-12 21:33:40 +0900201 if (!isMacAddress(addr)) {
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900202 return TYPE_UNKNOWN;
Hugo Benichi59c8e422017-10-12 21:33:40 +0900203 }
Hugo Benichi48872c62018-01-12 09:46:29 +0900204 return MacAddress.fromBytes(addr).getAddressType();
Hugo Benichiac52e402017-11-09 00:22:25 +0900205 }
206
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900207 /**
208 * Converts a String representation of a MAC address to a byte array representation.
209 * A valid String representation for a MacAddress is a series of 6 values in the
210 * range [0,ff] printed in hexadecimal and joined by ':' characters.
211 *
212 * @param addr a String representation of a MAC address.
213 * @return the byte representation of the MAC address.
214 * @throws IllegalArgumentException if the given String is not a valid representation.
215 *
216 * @hide
217 */
Hugo Benichi48872c62018-01-12 09:46:29 +0900218 public static @NonNull byte[] byteAddrFromStringAddr(String addr) {
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900219 Preconditions.checkNotNull(addr);
Hugo Benichiac52e402017-11-09 00:22:25 +0900220 String[] parts = addr.split(":");
221 if (parts.length != ETHER_ADDR_LEN) {
222 throw new IllegalArgumentException(addr + " was not a valid MAC address");
Hugo Benichi59c8e422017-10-12 21:33:40 +0900223 }
Hugo Benichiac52e402017-11-09 00:22:25 +0900224 byte[] bytes = new byte[ETHER_ADDR_LEN];
225 for (int i = 0; i < ETHER_ADDR_LEN; i++) {
226 int x = Integer.valueOf(parts[i], 16);
227 if (x < 0 || 0xff < x) {
228 throw new IllegalArgumentException(addr + "was not a valid MAC address");
229 }
230 bytes[i] = (byte) x;
231 }
232 return bytes;
233 }
234
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900235 /**
236 * Converts a byte array representation of a MAC address to a String representation made
237 * of 6 hexadecimal numbers in [0,ff] joined by ':' characters.
238 * A valid byte array representation for a MacAddress is a non-null array of length 6.
239 *
240 * @param addr a byte array representation of a MAC address.
241 * @return the String representation of the MAC address.
242 * @throws IllegalArgumentException if the given byte array is not a valid representation.
243 *
244 * @hide
245 */
Hugo Benichi48872c62018-01-12 09:46:29 +0900246 public static @NonNull String stringAddrFromByteAddr(byte[] addr) {
Hugo Benichiac52e402017-11-09 00:22:25 +0900247 if (!isMacAddress(addr)) {
248 return null;
249 }
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900250 return String.format("%02x:%02x:%02x:%02x:%02x:%02x",
251 addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
Hugo Benichiac52e402017-11-09 00:22:25 +0900252 }
253
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900254 private static byte[] byteAddrFromLongAddr(long addr) {
Hugo Benichiac52e402017-11-09 00:22:25 +0900255 byte[] bytes = new byte[ETHER_ADDR_LEN];
256 int index = ETHER_ADDR_LEN;
257 while (index-- > 0) {
258 bytes[index] = (byte) addr;
259 addr = addr >> 8;
260 }
261 return bytes;
262 }
263
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900264 private static long longAddrFromByteAddr(byte[] addr) {
265 Preconditions.checkNotNull(addr);
Hugo Benichiac52e402017-11-09 00:22:25 +0900266 if (!isMacAddress(addr)) {
267 throw new IllegalArgumentException(
268 Arrays.toString(addr) + " was not a valid MAC address");
269 }
270 long longAddr = 0;
271 for (byte b : addr) {
272 longAddr = (longAddr << 8) + BitUtils.uint8(b);
273 }
274 return longAddr;
275 }
276
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900277 // Internal conversion function equivalent to longAddrFromByteAddr(byteAddrFromStringAddr(addr))
278 // that avoids the allocation of an intermediary byte[].
279 private static long longAddrFromStringAddr(String addr) {
280 Preconditions.checkNotNull(addr);
Hugo Benichiac52e402017-11-09 00:22:25 +0900281 String[] parts = addr.split(":");
282 if (parts.length != ETHER_ADDR_LEN) {
283 throw new IllegalArgumentException(addr + " was not a valid MAC address");
284 }
285 long longAddr = 0;
Hugo Benichid2c5b192017-12-05 13:14:08 +0900286 for (int i = 0; i < parts.length; i++) {
287 int x = Integer.valueOf(parts[i], 16);
Hugo Benichiac52e402017-11-09 00:22:25 +0900288 if (x < 0 || 0xff < x) {
289 throw new IllegalArgumentException(addr + "was not a valid MAC address");
290 }
291 longAddr = x + (longAddr << 8);
292 }
293 return longAddr;
294 }
295
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900296 // Internal conversion function equivalent to stringAddrFromByteAddr(byteAddrFromLongAddr(addr))
297 // that avoids the allocation of an intermediary byte[].
Hugo Benichi48872c62018-01-12 09:46:29 +0900298 private static @NonNull String stringAddrFromLongAddr(long addr) {
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900299 return String.format("%02x:%02x:%02x:%02x:%02x:%02x",
300 (addr >> 40) & 0xff,
301 (addr >> 32) & 0xff,
302 (addr >> 24) & 0xff,
303 (addr >> 16) & 0xff,
304 (addr >> 8) & 0xff,
305 addr & 0xff);
Hugo Benichiac52e402017-11-09 00:22:25 +0900306 }
307
308 /**
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900309 * Creates a MacAddress from the given String representation. A valid String representation
310 * for a MacAddress is a series of 6 values in the range [0,ff] printed in hexadecimal
311 * and joined by ':' characters.
312 *
313 * @param addr a String representation of a MAC address.
314 * @return the MacAddress corresponding to the given String representation.
315 * @throws IllegalArgumentException if the given String is not a valid representation.
Hugo Benichiac52e402017-11-09 00:22:25 +0900316 */
Hugo Benichi48872c62018-01-12 09:46:29 +0900317 public static @NonNull MacAddress fromString(@NonNull String addr) {
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900318 return new MacAddress(longAddrFromStringAddr(addr));
Hugo Benichiac52e402017-11-09 00:22:25 +0900319 }
320
321 /**
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900322 * Creates a MacAddress from the given byte array representation.
323 * A valid byte array representation for a MacAddress is a non-null array of length 6.
324 *
325 * @param addr a byte array representation of a MAC address.
326 * @return the MacAddress corresponding to the given byte array representation.
327 * @throws IllegalArgumentException if the given byte array is not a valid representation.
Hugo Benichiac52e402017-11-09 00:22:25 +0900328 */
Hugo Benichi48872c62018-01-12 09:46:29 +0900329 public static @NonNull MacAddress fromBytes(@NonNull byte[] addr) {
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900330 return new MacAddress(longAddrFromByteAddr(addr));
331 }
332
333 /**
334 * Returns a generated MAC address whose 24 least significant bits constituting the
Jong Wook Kimf0a55cc2018-01-31 19:03:19 -0800335 * NIC part of the address are randomly selected and has Google OUI base.
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900336 *
337 * The locally assigned bit is always set to 1. The multicast bit is always set to 0.
338 *
Jong Wook Kimf0a55cc2018-01-31 19:03:19 -0800339 * @return a random locally assigned, unicast MacAddress with Google OUI.
340 *
341 * @hide
342 */
343 public static @NonNull MacAddress createRandomUnicastAddressWithGoogleBase() {
344 return createRandomUnicastAddress(BASE_GOOGLE_MAC, new SecureRandom());
345 }
346
347 /**
348 * Returns a generated MAC address whose 46 bits, excluding the locally assigned bit and the
349 * unicast bit, are randomly selected.
350 *
351 * The locally assigned bit is always set to 1. The multicast bit is always set to 0.
352 *
353 * @return a random locally assigned, unicast MacAddress.
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900354 *
355 * @hide
356 */
Hugo Benichi48872c62018-01-12 09:46:29 +0900357 public static @NonNull MacAddress createRandomUnicastAddress() {
Jong Wook Kimf0a55cc2018-01-31 19:03:19 -0800358 SecureRandom r = new SecureRandom();
359 long addr = r.nextLong() & VALID_LONG_MASK;
360 addr |= LOCALLY_ASSIGNED_MASK;
361 addr &= ~MULTICAST_MASK;
362 return new MacAddress(addr);
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900363 }
364
365 /**
366 * Returns a randomly generated MAC address using the given Random object and the same
367 * OUI values as the given MacAddress.
368 *
369 * The locally assigned bit is always set to 1. The multicast bit is always set to 0.
370 *
371 * @param base a base MacAddress whose OUI is used for generating the random address.
372 * @param r a standard Java Random object used for generating the random address.
373 * @return a random locally assigned MacAddress.
374 *
375 * @hide
376 */
Hugo Benichi48872c62018-01-12 09:46:29 +0900377 public static @NonNull MacAddress createRandomUnicastAddress(MacAddress base, Random r) {
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900378 long addr = (base.mAddr & OUI_MASK) | (NIC_MASK & r.nextLong());
Jong Wook Kimf0a55cc2018-01-31 19:03:19 -0800379 addr |= LOCALLY_ASSIGNED_MASK;
380 addr &= ~MULTICAST_MASK;
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900381 return new MacAddress(addr);
Hugo Benichiac52e402017-11-09 00:22:25 +0900382 }
383
384 // Convenience function for working around the lack of byte literals.
385 private static byte[] addr(int... in) {
386 if (in.length != ETHER_ADDR_LEN) {
387 throw new IllegalArgumentException(Arrays.toString(in)
388 + " was not an array with length equal to " + ETHER_ADDR_LEN);
389 }
390 byte[] out = new byte[ETHER_ADDR_LEN];
391 for (int i = 0; i < ETHER_ADDR_LEN; i++) {
392 out[i] = (byte) in[i];
393 }
394 return out;
Hugo Benichi59c8e422017-10-12 21:33:40 +0900395 }
396}