summaryrefslogtreecommitdiff
path: root/android/net/MacAddress.java
blob: d6992aaede5f2d13d11049bd4e277325e581b326 (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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
/*
 * Copyright 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.net;

import android.annotation.IntDef;
import android.os.Parcel;
import android.os.Parcelable;

import com.android.internal.util.BitUtils;
import com.android.internal.util.Preconditions;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.Random;

/**
 * Representation of a MAC address.
 *
 * This class only supports 48 bits long addresses and does not support 64 bits long addresses.
 * Instances of this class are immutable.
 */
public final class MacAddress implements Parcelable {

    private static final int ETHER_ADDR_LEN = 6;
    private static final byte[] ETHER_ADDR_BROADCAST = addr(0xff, 0xff, 0xff, 0xff, 0xff, 0xff);

    /**
     * The MacAddress representing the unique broadcast MAC address.
     */
    public static final MacAddress BROADCAST_ADDRESS = MacAddress.fromBytes(ETHER_ADDR_BROADCAST);

    /**
     * The MacAddress zero MAC address.
     * @hide
     */
    public static final MacAddress ALL_ZEROS_ADDRESS = new MacAddress(0);

    /** @hide */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(prefix = { "TYPE_" }, value = {
            TYPE_UNKNOWN,
            TYPE_UNICAST,
            TYPE_MULTICAST,
            TYPE_BROADCAST,
    })
    public @interface MacAddressType { }

    /** Indicates a MAC address of unknown type. */
    public static final int TYPE_UNKNOWN = 0;
    /** Indicates a MAC address is a unicast address. */
    public static final int TYPE_UNICAST = 1;
    /** Indicates a MAC address is a multicast address. */
    public static final int TYPE_MULTICAST = 2;
    /** Indicates a MAC address is the broadcast address. */
    public static final int TYPE_BROADCAST = 3;

    private static final long VALID_LONG_MASK = (1L << 48) - 1;
    private static final long LOCALLY_ASSIGNED_MASK = MacAddress.fromString("2:0:0:0:0:0").mAddr;
    private static final long MULTICAST_MASK = MacAddress.fromString("1:0:0:0:0:0").mAddr;
    private static final long OUI_MASK = MacAddress.fromString("ff:ff:ff:0:0:0").mAddr;
    private static final long NIC_MASK = MacAddress.fromString("0:0:0:ff:ff:ff").mAddr;
    private static final MacAddress BASE_GOOGLE_MAC = MacAddress.fromString("da:a1:19:0:0:0");

    // Internal representation of the MAC address as a single 8 byte long.
    // The encoding scheme sets the two most significant bytes to 0. The 6 bytes of the
    // MAC address are encoded in the 6 least significant bytes of the long, where the first
    // byte of the array is mapped to the 3rd highest logical byte of the long, the second
    // byte of the array is mapped to the 4th highest logical byte of the long, and so on.
    private final long mAddr;

    private MacAddress(long addr) {
        mAddr = (VALID_LONG_MASK & addr);
    }

    /**
     * Returns the type of this address.
     *
     * @return the int constant representing the MAC address type of this MacAddress.
     */
    public @MacAddressType int addressType() {
        if (equals(BROADCAST_ADDRESS)) {
            return TYPE_BROADCAST;
        }
        if (isMulticastAddress()) {
            return TYPE_MULTICAST;
        }
        return TYPE_UNICAST;
    }

    /**
     * @return true if this MacAddress is a multicast address.
     * @hide
     */
    public boolean isMulticastAddress() {
        return (mAddr & MULTICAST_MASK) != 0;
    }

    /**
     * @return true if this MacAddress is a locally assigned address.
     */
    public boolean isLocallyAssigned() {
        return (mAddr & LOCALLY_ASSIGNED_MASK) != 0;
    }

    /**
     * @return a byte array representation of this MacAddress.
     */
    public byte[] toByteArray() {
        return byteAddrFromLongAddr(mAddr);
    }

    @Override
    public String toString() {
        return stringAddrFromLongAddr(mAddr);
    }

    /**
     * @return a String representation of the OUI part of this MacAddress made of 3 hexadecimal
     * numbers in [0,ff] joined by ':' characters.
     */
    public String toOuiString() {
        return String.format(
                "%02x:%02x:%02x", (mAddr >> 40) & 0xff, (mAddr >> 32) & 0xff, (mAddr >> 24) & 0xff);
    }

    @Override
    public int hashCode() {
        return (int) ((mAddr >> 32) ^ mAddr);
    }

    @Override
    public boolean equals(Object o) {
        return (o instanceof MacAddress) && ((MacAddress) o).mAddr == mAddr;
    }

    @Override
    public void writeToParcel(Parcel out, int flags) {
        out.writeLong(mAddr);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    public static final Parcelable.Creator<MacAddress> CREATOR =
            new Parcelable.Creator<MacAddress>() {
                public MacAddress createFromParcel(Parcel in) {
                    return new MacAddress(in.readLong());
                }

                public MacAddress[] newArray(int size) {
                    return new MacAddress[size];
                }
            };

    /**
     * Returns true if the given byte array is an valid MAC address.
     * A valid byte array representation for a MacAddress is a non-null array of length 6.
     *
     * @param addr a byte array.
     * @return true if the given byte array is not null and has the length of a MAC address.
     *
     * @hide
     */
    public static boolean isMacAddress(byte[] addr) {
        return addr != null && addr.length == ETHER_ADDR_LEN;
    }

    /**
     * Returns the MAC address type of the MAC address represented by the given byte array,
     * or null if the given byte array does not represent a MAC address.
     * A valid byte array representation for a MacAddress is a non-null array of length 6.
     *
     * @param addr a byte array representing a MAC address.
     * @return the int constant representing the MAC address type of the MAC address represented
     * by the given byte array, or type UNKNOWN if the byte array is not a valid MAC address.
     *
     * @hide
     */
    public static int macAddressType(byte[] addr) {
        if (!isMacAddress(addr)) {
            return TYPE_UNKNOWN;
        }
        return MacAddress.fromBytes(addr).addressType();
    }

    /**
     * Converts a String representation of a MAC address to a byte array representation.
     * A valid String representation for a MacAddress is a series of 6 values in the
     * range [0,ff] printed in hexadecimal and joined by ':' characters.
     *
     * @param addr a String representation of a MAC address.
     * @return the byte representation of the MAC address.
     * @throws IllegalArgumentException if the given String is not a valid representation.
     *
     * @hide
     */
    public static byte[] byteAddrFromStringAddr(String addr) {
        Preconditions.checkNotNull(addr);
        String[] parts = addr.split(":");
        if (parts.length != ETHER_ADDR_LEN) {
            throw new IllegalArgumentException(addr + " was not a valid MAC address");
        }
        byte[] bytes = new byte[ETHER_ADDR_LEN];
        for (int i = 0; i < ETHER_ADDR_LEN; i++) {
            int x = Integer.valueOf(parts[i], 16);
            if (x < 0 || 0xff < x) {
                throw new IllegalArgumentException(addr + "was not a valid MAC address");
            }
            bytes[i] = (byte) x;
        }
        return bytes;
    }

    /**
     * Converts a byte array representation of a MAC address to a String representation made
     * of 6 hexadecimal numbers in [0,ff] joined by ':' characters.
     * A valid byte array representation for a MacAddress is a non-null array of length 6.
     *
     * @param addr a byte array representation of a MAC address.
     * @return the String representation of the MAC address.
     * @throws IllegalArgumentException if the given byte array is not a valid representation.
     *
     * @hide
     */
    public static String stringAddrFromByteAddr(byte[] addr) {
        if (!isMacAddress(addr)) {
            return null;
        }
        return String.format("%02x:%02x:%02x:%02x:%02x:%02x",
                addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
    }

    private static byte[] byteAddrFromLongAddr(long addr) {
        byte[] bytes = new byte[ETHER_ADDR_LEN];
        int index = ETHER_ADDR_LEN;
        while (index-- > 0) {
            bytes[index] = (byte) addr;
            addr = addr >> 8;
        }
        return bytes;
    }

    private static long longAddrFromByteAddr(byte[] addr) {
        Preconditions.checkNotNull(addr);
        if (!isMacAddress(addr)) {
            throw new IllegalArgumentException(
                    Arrays.toString(addr) + " was not a valid MAC address");
        }
        long longAddr = 0;
        for (byte b : addr) {
            longAddr = (longAddr << 8) + BitUtils.uint8(b);
        }
        return longAddr;
    }

    // Internal conversion function equivalent to longAddrFromByteAddr(byteAddrFromStringAddr(addr))
    // that avoids the allocation of an intermediary byte[].
    private static long longAddrFromStringAddr(String addr) {
        Preconditions.checkNotNull(addr);
        String[] parts = addr.split(":");
        if (parts.length != ETHER_ADDR_LEN) {
            throw new IllegalArgumentException(addr + " was not a valid MAC address");
        }
        long longAddr = 0;
        for (int i = 0; i < parts.length; i++) {
            int x = Integer.valueOf(parts[i], 16);
            if (x < 0 || 0xff < x) {
                throw new IllegalArgumentException(addr + "was not a valid MAC address");
            }
            longAddr = x + (longAddr << 8);
        }
        return longAddr;
    }

    // Internal conversion function equivalent to stringAddrFromByteAddr(byteAddrFromLongAddr(addr))
    // that avoids the allocation of an intermediary byte[].
    private static String stringAddrFromLongAddr(long addr) {
        return String.format("%02x:%02x:%02x:%02x:%02x:%02x",
                (addr >> 40) & 0xff,
                (addr >> 32) & 0xff,
                (addr >> 24) & 0xff,
                (addr >> 16) & 0xff,
                (addr >> 8) & 0xff,
                addr & 0xff);
    }

    /**
     * Creates a MacAddress from the given String representation. A valid String representation
     * for a MacAddress is a series of 6 values in the range [0,ff] printed in hexadecimal
     * and joined by ':' characters.
     *
     * @param addr a String representation of a MAC address.
     * @return the MacAddress corresponding to the given String representation.
     * @throws IllegalArgumentException if the given String is not a valid representation.
     */
    public static MacAddress fromString(String addr) {
        return new MacAddress(longAddrFromStringAddr(addr));
    }

    /**
     * Creates a MacAddress from the given byte array representation.
     * A valid byte array representation for a MacAddress is a non-null array of length 6.
     *
     * @param addr a byte array representation of a MAC address.
     * @return the MacAddress corresponding to the given byte array representation.
     * @throws IllegalArgumentException if the given byte array is not a valid representation.
     */
    public static MacAddress fromBytes(byte[] addr) {
        return new MacAddress(longAddrFromByteAddr(addr));
    }

    /**
     * Returns a generated MAC address whose 24 least significant bits constituting the
     * NIC part of the address are randomly selected.
     *
     * The locally assigned bit is always set to 1. The multicast bit is always set to 0.
     *
     * @return a random locally assigned MacAddress.
     *
     * @hide
     */
    public static MacAddress createRandomUnicastAddress() {
        return createRandomUnicastAddress(BASE_GOOGLE_MAC, new Random());
    }

    /**
     * Returns a randomly generated MAC address using the given Random object and the same
     * OUI values as the given MacAddress.
     *
     * The locally assigned bit is always set to 1. The multicast bit is always set to 0.
     *
     * @param base a base MacAddress whose OUI is used for generating the random address.
     * @param r a standard Java Random object used for generating the random address.
     * @return a random locally assigned MacAddress.
     *
     * @hide
     */
    public static MacAddress createRandomUnicastAddress(MacAddress base, Random r) {
        long addr = (base.mAddr & OUI_MASK) | (NIC_MASK & r.nextLong());
        addr = addr | LOCALLY_ASSIGNED_MASK;
        addr = addr & ~MULTICAST_MASK;
        return new MacAddress(addr);
    }

    // Convenience function for working around the lack of byte literals.
    private static byte[] addr(int... in) {
        if (in.length != ETHER_ADDR_LEN) {
            throw new IllegalArgumentException(Arrays.toString(in)
                    + " was not an array with length equal to " + ETHER_ADDR_LEN);
        }
        byte[] out = new byte[ETHER_ADDR_LEN];
        for (int i = 0; i < ETHER_ADDR_LEN; i++) {
            out[i] = (byte) in[i];
        }
        return out;
    }
}