blob: 0e10c42e61db27e1a73945f99207acf503d1cb60 [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;
Etan Cohena4824cf2018-11-02 15:07:20 -070021import android.annotation.Nullable;
Artur Satayev26958002019-12-10 17:47:52 +000022import android.compat.annotation.UnsupportedAppUsage;
Aaron Huang94df39a2019-12-17 00:33:18 +080023import android.net.util.MacAddressUtils;
xshu683b7562019-08-16 10:20:22 -070024import android.net.wifi.WifiInfo;
Hugo Benichiac52e402017-11-09 00:22:25 +090025import android.os.Parcel;
26import android.os.Parcelable;
27
Hugo Benichi84bb7fc2017-11-16 14:40:16 +090028import com.android.internal.util.Preconditions;
Hugo Benichi59c8e422017-10-12 21:33:40 +090029
Hugo Benichi84bb7fc2017-11-16 14:40:16 +090030import java.lang.annotation.Retention;
31import java.lang.annotation.RetentionPolicy;
Etan Cohena4824cf2018-11-02 15:07:20 -070032import java.net.Inet6Address;
33import java.net.UnknownHostException;
Jong Wook Kimf0a55cc2018-01-31 19:03:19 -080034import java.security.SecureRandom;
Hugo Benichi59c8e422017-10-12 21:33:40 +090035import java.util.Arrays;
36
37/**
Hugo Benichi84bb7fc2017-11-16 14:40:16 +090038 * Representation of a MAC address.
39 *
40 * This class only supports 48 bits long addresses and does not support 64 bits long addresses.
41 * Instances of this class are immutable.
Hugo Benichi59c8e422017-10-12 21:33:40 +090042 */
Hugo Benichiac52e402017-11-09 00:22:25 +090043public final class MacAddress implements Parcelable {
Hugo Benichi59c8e422017-10-12 21:33:40 +090044
45 private static final int ETHER_ADDR_LEN = 6;
Hugo Benichiac52e402017-11-09 00:22:25 +090046 private static final byte[] ETHER_ADDR_BROADCAST = addr(0xff, 0xff, 0xff, 0xff, 0xff, 0xff);
Hugo Benichi59c8e422017-10-12 21:33:40 +090047
Hugo Benichi84bb7fc2017-11-16 14:40:16 +090048 /**
49 * The MacAddress representing the unique broadcast MAC address.
50 */
51 public static final MacAddress BROADCAST_ADDRESS = MacAddress.fromBytes(ETHER_ADDR_BROADCAST);
Hugo Benichiac52e402017-11-09 00:22:25 +090052
Hugo Benichi84bb7fc2017-11-16 14:40:16 +090053 /**
54 * The MacAddress zero MAC address.
Remi NGUYEN VAN31f1d0c2019-01-20 12:52:43 +090055 *
56 * <p>Not publicly exposed or treated specially since the OUI 00:00:00 is registered.
Hugo Benichi84bb7fc2017-11-16 14:40:16 +090057 * @hide
58 */
Mathew Inwood53f089f2018-08-08 14:44:44 +010059 @UnsupportedAppUsage
Hugo Benichiac52e402017-11-09 00:22:25 +090060 public static final MacAddress ALL_ZEROS_ADDRESS = new MacAddress(0);
61
Hugo Benichi84bb7fc2017-11-16 14:40:16 +090062 /** @hide */
63 @Retention(RetentionPolicy.SOURCE)
64 @IntDef(prefix = { "TYPE_" }, value = {
65 TYPE_UNKNOWN,
66 TYPE_UNICAST,
67 TYPE_MULTICAST,
68 TYPE_BROADCAST,
69 })
70 public @interface MacAddressType { }
Hugo Benichi59c8e422017-10-12 21:33:40 +090071
Hugo Benichi48872c62018-01-12 09:46:29 +090072 /** @hide Indicates a MAC address of unknown type. */
Hugo Benichi84bb7fc2017-11-16 14:40:16 +090073 public static final int TYPE_UNKNOWN = 0;
74 /** Indicates a MAC address is a unicast address. */
75 public static final int TYPE_UNICAST = 1;
76 /** Indicates a MAC address is a multicast address. */
77 public static final int TYPE_MULTICAST = 2;
78 /** Indicates a MAC address is the broadcast address. */
79 public static final int TYPE_BROADCAST = 3;
Hugo Benichiac52e402017-11-09 00:22:25 +090080
Hugo Benichi84bb7fc2017-11-16 14:40:16 +090081 private static final long VALID_LONG_MASK = (1L << 48) - 1;
82 private static final long LOCALLY_ASSIGNED_MASK = MacAddress.fromString("2:0:0:0:0:0").mAddr;
83 private static final long MULTICAST_MASK = MacAddress.fromString("1:0:0:0:0:0").mAddr;
84 private static final long OUI_MASK = MacAddress.fromString("ff:ff:ff:0:0:0").mAddr;
85 private static final long NIC_MASK = MacAddress.fromString("0:0:0:ff:ff:ff").mAddr;
86 private static final MacAddress BASE_GOOGLE_MAC = MacAddress.fromString("da:a1:19:0:0:0");
xshub4836072019-08-21 13:40:18 -070087 /** Default wifi MAC address used for a special purpose **/
88 private static final MacAddress DEFAULT_MAC_ADDRESS =
89 MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS);
Hugo Benichi84bb7fc2017-11-16 14:40:16 +090090
91 // Internal representation of the MAC address as a single 8 byte long.
Hugo Benichiac52e402017-11-09 00:22:25 +090092 // The encoding scheme sets the two most significant bytes to 0. The 6 bytes of the
Hugo Benichi84bb7fc2017-11-16 14:40:16 +090093 // MAC address are encoded in the 6 least significant bytes of the long, where the first
Hugo Benichiac52e402017-11-09 00:22:25 +090094 // byte of the array is mapped to the 3rd highest logical byte of the long, the second
95 // byte of the array is mapped to the 4th highest logical byte of the long, and so on.
96 private final long mAddr;
97
98 private MacAddress(long addr) {
Hugo Benichi84bb7fc2017-11-16 14:40:16 +090099 mAddr = (VALID_LONG_MASK & addr);
Hugo Benichiac52e402017-11-09 00:22:25 +0900100 }
101
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900102 /**
103 * Returns the type of this address.
104 *
105 * @return the int constant representing the MAC address type of this MacAddress.
106 */
Hugo Benichi48872c62018-01-12 09:46:29 +0900107 public @MacAddressType int getAddressType() {
Hugo Benichiac52e402017-11-09 00:22:25 +0900108 if (equals(BROADCAST_ADDRESS)) {
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900109 return TYPE_BROADCAST;
Hugo Benichiac52e402017-11-09 00:22:25 +0900110 }
Aaron Huang94df39a2019-12-17 00:33:18 +0800111 if ((mAddr & MULTICAST_MASK) != 0) {
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900112 return TYPE_MULTICAST;
Hugo Benichiac52e402017-11-09 00:22:25 +0900113 }
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900114 return TYPE_UNICAST;
Hugo Benichiac52e402017-11-09 00:22:25 +0900115 }
116
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900117 /**
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900118 * @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
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -0700165 public static final @android.annotation.NonNull Parcelable.Creator<MacAddress> CREATOR =
Hugo Benichiac52e402017-11-09 00:22:25 +0900166 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) {
Aaron Huang94df39a2019-12-17 00:33:18 +0800186 return MacAddressUtils.isMacAddress(addr);
Hugo Benichi59c8e422017-10-12 21:33:40 +0900187 }
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) {
Aaron Huang94df39a2019-12-17 00:33:18 +0800255 return MacAddressUtils.byteAddrFromLongAddr(addr);
Hugo Benichiac52e402017-11-09 00:22:25 +0900256 }
257
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900258 private static long longAddrFromByteAddr(byte[] addr) {
Aaron Huang94df39a2019-12-17 00:33:18 +0800259 return MacAddressUtils.longAddrFromByteAddr(addr);
Hugo Benichiac52e402017-11-09 00:22:25 +0900260 }
261
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900262 // Internal conversion function equivalent to longAddrFromByteAddr(byteAddrFromStringAddr(addr))
263 // that avoids the allocation of an intermediary byte[].
264 private static long longAddrFromStringAddr(String addr) {
265 Preconditions.checkNotNull(addr);
Hugo Benichiac52e402017-11-09 00:22:25 +0900266 String[] parts = addr.split(":");
267 if (parts.length != ETHER_ADDR_LEN) {
268 throw new IllegalArgumentException(addr + " was not a valid MAC address");
269 }
270 long longAddr = 0;
Hugo Benichid2c5b192017-12-05 13:14:08 +0900271 for (int i = 0; i < parts.length; i++) {
272 int x = Integer.valueOf(parts[i], 16);
Hugo Benichiac52e402017-11-09 00:22:25 +0900273 if (x < 0 || 0xff < x) {
274 throw new IllegalArgumentException(addr + "was not a valid MAC address");
275 }
276 longAddr = x + (longAddr << 8);
277 }
278 return longAddr;
279 }
280
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900281 // Internal conversion function equivalent to stringAddrFromByteAddr(byteAddrFromLongAddr(addr))
282 // that avoids the allocation of an intermediary byte[].
Hugo Benichi48872c62018-01-12 09:46:29 +0900283 private static @NonNull String stringAddrFromLongAddr(long addr) {
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900284 return String.format("%02x:%02x:%02x:%02x:%02x:%02x",
285 (addr >> 40) & 0xff,
286 (addr >> 32) & 0xff,
287 (addr >> 24) & 0xff,
288 (addr >> 16) & 0xff,
289 (addr >> 8) & 0xff,
290 addr & 0xff);
Hugo Benichiac52e402017-11-09 00:22:25 +0900291 }
292
293 /**
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900294 * Creates a MacAddress from the given String representation. A valid String representation
295 * for a MacAddress is a series of 6 values in the range [0,ff] printed in hexadecimal
296 * and joined by ':' characters.
297 *
298 * @param addr a String representation of a MAC address.
299 * @return the MacAddress corresponding to the given String representation.
300 * @throws IllegalArgumentException if the given String is not a valid representation.
Hugo Benichiac52e402017-11-09 00:22:25 +0900301 */
Hugo Benichi48872c62018-01-12 09:46:29 +0900302 public static @NonNull MacAddress fromString(@NonNull String addr) {
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900303 return new MacAddress(longAddrFromStringAddr(addr));
Hugo Benichiac52e402017-11-09 00:22:25 +0900304 }
305
306 /**
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900307 * Creates a MacAddress from the given byte array representation.
308 * A valid byte array representation for a MacAddress is a non-null array of length 6.
309 *
310 * @param addr a byte array representation of a MAC address.
311 * @return the MacAddress corresponding to the given byte array representation.
312 * @throws IllegalArgumentException if the given byte array is not a valid representation.
Hugo Benichiac52e402017-11-09 00:22:25 +0900313 */
Hugo Benichi48872c62018-01-12 09:46:29 +0900314 public static @NonNull MacAddress fromBytes(@NonNull byte[] addr) {
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900315 return new MacAddress(longAddrFromByteAddr(addr));
316 }
317
318 /**
319 * Returns a generated MAC address whose 24 least significant bits constituting the
Jong Wook Kimf0a55cc2018-01-31 19:03:19 -0800320 * NIC part of the address are randomly selected and has Google OUI base.
Hugo Benichi84bb7fc2017-11-16 14:40:16 +0900321 *
322 * The locally assigned bit is always set to 1. The multicast bit is always set to 0.
323 *
Jong Wook Kimf0a55cc2018-01-31 19:03:19 -0800324 * @return a random locally assigned, unicast MacAddress with Google OUI.
325 *
326 * @hide
327 */
328 public static @NonNull MacAddress createRandomUnicastAddressWithGoogleBase() {
Aaron Huang94df39a2019-12-17 00:33:18 +0800329 return MacAddressUtils.createRandomUnicastAddress(BASE_GOOGLE_MAC, new SecureRandom());
Hugo Benichiac52e402017-11-09 00:22:25 +0900330 }
331
332 // Convenience function for working around the lack of byte literals.
333 private static byte[] addr(int... in) {
334 if (in.length != ETHER_ADDR_LEN) {
335 throw new IllegalArgumentException(Arrays.toString(in)
336 + " was not an array with length equal to " + ETHER_ADDR_LEN);
337 }
338 byte[] out = new byte[ETHER_ADDR_LEN];
339 for (int i = 0; i < ETHER_ADDR_LEN; i++) {
340 out[i] = (byte) in[i];
341 }
342 return out;
Hugo Benichi59c8e422017-10-12 21:33:40 +0900343 }
Roshan Pius99cfe092018-10-05 09:42:19 -0700344
345 /**
346 * Checks if this MAC Address matches the provided range.
347 *
348 * @param baseAddress MacAddress representing the base address to compare with.
349 * @param mask MacAddress representing the mask to use during comparison.
350 * @return true if this MAC Address matches the given range.
351 *
Roshan Pius99cfe092018-10-05 09:42:19 -0700352 */
353 public boolean matches(@NonNull MacAddress baseAddress, @NonNull MacAddress mask) {
354 Preconditions.checkNotNull(baseAddress);
355 Preconditions.checkNotNull(mask);
356 return (mAddr & mask.mAddr) == (baseAddress.mAddr & mask.mAddr);
357 }
Etan Cohena4824cf2018-11-02 15:07:20 -0700358
359 /**
360 * Create a link-local Inet6Address from the MAC address. The EUI-48 MAC address is converted
361 * to an EUI-64 MAC address per RFC 4291. The resulting EUI-64 is used to construct a link-local
362 * IPv6 address per RFC 4862.
363 *
364 * @return A link-local Inet6Address constructed from the MAC address.
Etan Cohena4824cf2018-11-02 15:07:20 -0700365 */
366 public @Nullable Inet6Address getLinkLocalIpv6FromEui48Mac() {
367 byte[] macEui48Bytes = toByteArray();
368 byte[] addr = new byte[16];
369
370 addr[0] = (byte) 0xfe;
371 addr[1] = (byte) 0x80;
372 addr[8] = (byte) (macEui48Bytes[0] ^ (byte) 0x02); // flip the link-local bit
373 addr[9] = macEui48Bytes[1];
374 addr[10] = macEui48Bytes[2];
375 addr[11] = (byte) 0xff;
376 addr[12] = (byte) 0xfe;
377 addr[13] = macEui48Bytes[3];
378 addr[14] = macEui48Bytes[4];
379 addr[15] = macEui48Bytes[5];
380
381 try {
382 return Inet6Address.getByAddress(null, addr, 0);
383 } catch (UnknownHostException e) {
384 return null;
385 }
386 }
Hugo Benichi59c8e422017-10-12 21:33:40 +0900387}