Move util classes to their destination package
Test: builds Change-Id: I93e9cfd968e08f983e3fd9f7196c455c79a0f32d (cherry picked from commit 7c9c12694aec7d688496a634f165661e0681773e)
This commit is contained in:
parent
6d3dbfee47
commit
ba79273382
18 changed files with 33 additions and 36 deletions
|
|
@ -86,6 +86,13 @@ java_defaults {
|
|||
],
|
||||
}
|
||||
|
||||
// Rules for moving classes part of the lib that haven't been
|
||||
// renamed yet for lack of time
|
||||
java_defaults {
|
||||
name: "net-utils-jarjar-shared",
|
||||
jarjar_rules: "jarjar-rules-shared.txt",
|
||||
}
|
||||
|
||||
java_library {
|
||||
name: "net-tests-utils",
|
||||
srcs: [
|
||||
|
|
@ -164,8 +171,6 @@ filegroup {
|
|||
filegroup {
|
||||
name: "net-utils-framework-wifi-common-srcs",
|
||||
srcs: [
|
||||
"framework/android/net/util/nsd/DnsSdTxtRecord.java",
|
||||
"framework/android/net/util/MacAddressUtils.java",
|
||||
"framework/com/android/net/module/util/**/*.java",
|
||||
],
|
||||
path: "framework",
|
||||
|
|
@ -181,7 +186,7 @@ filegroup {
|
|||
name: "net-utils-wifi-service-common-srcs",
|
||||
srcs: [
|
||||
"device/android/net/NetworkFactory.java",
|
||||
"framework/android/net/util/NetUtils.java",
|
||||
"framework/com/android/net/module/util/NetUtils.java",
|
||||
],
|
||||
visibility: [
|
||||
"//frameworks/opt/net/wifi/service",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,268 @@
|
|||
/*
|
||||
* Copyright (C) 2016 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 com.android.net.module.util;
|
||||
|
||||
import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR;
|
||||
import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.net.util.SocketUtils;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.MessageQueue;
|
||||
import android.system.ErrnoException;
|
||||
import android.system.OsConstants;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
/**
|
||||
* This class encapsulates the mechanics of registering a file descriptor
|
||||
* with a thread's Looper and handling read events (and errors).
|
||||
*
|
||||
* Subclasses MUST implement createFd() and SHOULD override handlePacket(). They MAY override
|
||||
* onStop() and onStart().
|
||||
*
|
||||
* Subclasses can expect a call life-cycle like the following:
|
||||
*
|
||||
* [1] when a client calls start(), createFd() is called, followed by the onStart() hook if all
|
||||
* goes well. Implementations may override onStart() for additional initialization.
|
||||
*
|
||||
* [2] yield, waiting for read event or error notification:
|
||||
*
|
||||
* [a] readPacket() && handlePacket()
|
||||
*
|
||||
* [b] if (no error):
|
||||
* goto 2
|
||||
* else:
|
||||
* goto 3
|
||||
*
|
||||
* [3] when a client calls stop(), the onStop() hook is called (unless already stopped or never
|
||||
* started). Implementations may override onStop() for additional cleanup.
|
||||
*
|
||||
* The packet receive buffer is recycled on every read call, so subclasses
|
||||
* should make any copies they would like inside their handlePacket()
|
||||
* implementation.
|
||||
*
|
||||
* All public methods MUST only be called from the same thread with which
|
||||
* the Handler constructor argument is associated.
|
||||
*
|
||||
* @param <BufferType> the type of the buffer used to read data.
|
||||
*/
|
||||
public abstract class FdEventsReader<BufferType> {
|
||||
private static final int FD_EVENTS = EVENT_INPUT | EVENT_ERROR;
|
||||
private static final int UNREGISTER_THIS_FD = 0;
|
||||
|
||||
@NonNull
|
||||
private final Handler mHandler;
|
||||
@NonNull
|
||||
private final MessageQueue mQueue;
|
||||
@NonNull
|
||||
private final BufferType mBuffer;
|
||||
@Nullable
|
||||
private FileDescriptor mFd;
|
||||
private long mPacketsReceived;
|
||||
|
||||
protected static void closeFd(FileDescriptor fd) {
|
||||
try {
|
||||
SocketUtils.closeSocket(fd);
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
protected FdEventsReader(@NonNull Handler h, @NonNull BufferType buffer) {
|
||||
mHandler = h;
|
||||
mQueue = mHandler.getLooper().getQueue();
|
||||
mBuffer = buffer;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@NonNull
|
||||
protected MessageQueue getMessageQueue() {
|
||||
return mQueue;
|
||||
}
|
||||
|
||||
/** Start this FdEventsReader. */
|
||||
public boolean start() {
|
||||
if (!onCorrectThread()) {
|
||||
throw new IllegalStateException("start() called from off-thread");
|
||||
}
|
||||
|
||||
return createAndRegisterFd();
|
||||
}
|
||||
|
||||
/** Stop this FdEventsReader and destroy the file descriptor. */
|
||||
public void stop() {
|
||||
if (!onCorrectThread()) {
|
||||
throw new IllegalStateException("stop() called from off-thread");
|
||||
}
|
||||
|
||||
unregisterAndDestroyFd();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Handler getHandler() {
|
||||
return mHandler;
|
||||
}
|
||||
|
||||
protected abstract int recvBufSize(@NonNull BufferType buffer);
|
||||
|
||||
/** Returns the size of the receive buffer. */
|
||||
public int recvBufSize() {
|
||||
return recvBufSize(mBuffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of successful calls to {@link #readPacket(FileDescriptor, Object)}.
|
||||
*
|
||||
* <p>A call was successful if {@link #readPacket(FileDescriptor, Object)} returned a value > 0.
|
||||
*/
|
||||
public final long numPacketsReceived() {
|
||||
return mPacketsReceived;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses MUST create the listening socket here, including setting all desired socket
|
||||
* options, interface or address/port binding, etc. The socket MUST be created nonblocking.
|
||||
*/
|
||||
@Nullable
|
||||
protected abstract FileDescriptor createFd();
|
||||
|
||||
/**
|
||||
* Implementations MUST return the bytes read or throw an Exception.
|
||||
*
|
||||
* <p>The caller may throw a {@link ErrnoException} with {@link OsConstants#EAGAIN} or
|
||||
* {@link OsConstants#EINTR}, in which case {@link FdEventsReader} will ignore the buffer
|
||||
* contents and respectively wait for further input or retry the read immediately. For all other
|
||||
* exceptions, the {@link FdEventsReader} will be stopped with no more interactions with this
|
||||
* method.
|
||||
*/
|
||||
protected abstract int readPacket(@NonNull FileDescriptor fd, @NonNull BufferType buffer)
|
||||
throws Exception;
|
||||
|
||||
/**
|
||||
* Called by the main loop for every packet. Any desired copies of
|
||||
* |recvbuf| should be made in here, as the underlying byte array is
|
||||
* reused across all reads.
|
||||
*/
|
||||
protected void handlePacket(@NonNull BufferType recvbuf, int length) {}
|
||||
|
||||
/**
|
||||
* Called by the main loop to log errors. In some cases |e| may be null.
|
||||
*/
|
||||
protected void logError(@NonNull String msg, @Nullable Exception e) {}
|
||||
|
||||
/**
|
||||
* Called by start(), if successful, just prior to returning.
|
||||
*/
|
||||
protected void onStart() {}
|
||||
|
||||
/**
|
||||
* Called by stop() just prior to returning.
|
||||
*/
|
||||
protected void onStop() {}
|
||||
|
||||
private boolean createAndRegisterFd() {
|
||||
if (mFd != null) return true;
|
||||
|
||||
try {
|
||||
mFd = createFd();
|
||||
} catch (Exception e) {
|
||||
logError("Failed to create socket: ", e);
|
||||
closeFd(mFd);
|
||||
mFd = null;
|
||||
}
|
||||
|
||||
if (mFd == null) return false;
|
||||
|
||||
getMessageQueue().addOnFileDescriptorEventListener(
|
||||
mFd,
|
||||
FD_EVENTS,
|
||||
(fd, events) -> {
|
||||
// Always call handleInput() so read/recvfrom are given
|
||||
// a proper chance to encounter a meaningful errno and
|
||||
// perhaps log a useful error message.
|
||||
if (!isRunning() || !handleInput()) {
|
||||
unregisterAndDestroyFd();
|
||||
return UNREGISTER_THIS_FD;
|
||||
}
|
||||
return FD_EVENTS;
|
||||
});
|
||||
onStart();
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isRunning() {
|
||||
return (mFd != null) && mFd.valid();
|
||||
}
|
||||
|
||||
// Keep trying to read until we get EAGAIN/EWOULDBLOCK or some fatal error.
|
||||
private boolean handleInput() {
|
||||
while (isRunning()) {
|
||||
final int bytesRead;
|
||||
|
||||
try {
|
||||
bytesRead = readPacket(mFd, mBuffer);
|
||||
if (bytesRead < 1) {
|
||||
if (isRunning()) logError("Socket closed, exiting", null);
|
||||
break;
|
||||
}
|
||||
mPacketsReceived++;
|
||||
} catch (ErrnoException e) {
|
||||
if (e.errno == OsConstants.EAGAIN) {
|
||||
// We've read everything there is to read this time around.
|
||||
return true;
|
||||
} else if (e.errno == OsConstants.EINTR) {
|
||||
continue;
|
||||
} else {
|
||||
if (isRunning()) logError("readPacket error: ", e);
|
||||
break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (isRunning()) logError("readPacket error: ", e);
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
handlePacket(mBuffer, bytesRead);
|
||||
} catch (Exception e) {
|
||||
logError("handlePacket error: ", e);
|
||||
Log.wtf(FdEventsReader.class.getSimpleName(), "Error handling packet", e);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void unregisterAndDestroyFd() {
|
||||
if (mFd == null) return;
|
||||
|
||||
getMessageQueue().removeOnFileDescriptorEventListener(mFd);
|
||||
closeFd(mFd);
|
||||
mFd = null;
|
||||
onStop();
|
||||
}
|
||||
|
||||
private boolean onCorrectThread() {
|
||||
return (mHandler.getLooper() == Looper.myLooper());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright (C) 2018 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 com.android.net.module.util;
|
||||
|
||||
import static java.lang.Math.max;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.system.Os;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
|
||||
/**
|
||||
* Specialization of {@link FdEventsReader} that reads packets into a byte array.
|
||||
*
|
||||
* TODO: rename this class to something more correctly descriptive (something
|
||||
* like [or less horrible than] FdReadEventsHandler?).
|
||||
*/
|
||||
public abstract class PacketReader extends FdEventsReader<byte[]> {
|
||||
|
||||
public static final int DEFAULT_RECV_BUF_SIZE = 2 * 1024;
|
||||
|
||||
protected PacketReader(Handler h) {
|
||||
this(h, DEFAULT_RECV_BUF_SIZE);
|
||||
}
|
||||
|
||||
protected PacketReader(Handler h, int recvBufSize) {
|
||||
super(h, new byte[max(recvBufSize, DEFAULT_RECV_BUF_SIZE)]);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final int recvBufSize(byte[] buffer) {
|
||||
return buffer.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses MAY override this to change the default read() implementation
|
||||
* in favour of, say, recvfrom().
|
||||
*
|
||||
* Implementations MUST return the bytes read or throw an Exception.
|
||||
*/
|
||||
@Override
|
||||
protected int readPacket(FileDescriptor fd, byte[] packetBuffer) throws Exception {
|
||||
return Os.read(fd, packetBuffer, 0, packetBuffer.length);
|
||||
}
|
||||
}
|
||||
|
|
@ -16,13 +16,13 @@
|
|||
|
||||
package com.android.testutils;
|
||||
|
||||
import android.net.util.PacketReader;
|
||||
import android.os.Handler;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.net.module.util.ArrayTrackRecord;
|
||||
import com.android.net.module.util.PacketReader;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.FileOutputStream;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,325 @@
|
|||
/* -*- Mode: Java; tab-width: 4 -*-
|
||||
*
|
||||
* Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
|
||||
To do:
|
||||
- implement remove()
|
||||
- fix set() to replace existing values
|
||||
*/
|
||||
|
||||
package com.android.net.module.util;
|
||||
|
||||
import android.os.Parcelable;
|
||||
import android.os.Parcel;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* This class handles TXT record data for DNS based service discovery as specified at
|
||||
* http://tools.ietf.org/html/draft-cheshire-dnsext-dns-sd-11
|
||||
*
|
||||
* DNS-SD specifies that a TXT record corresponding to an SRV record consist of
|
||||
* a packed array of bytes, each preceded by a length byte. Each string
|
||||
* is an attribute-value pair.
|
||||
*
|
||||
* The DnsSdTxtRecord object stores the entire TXT data as a single byte array, traversing it
|
||||
* as need be to implement its various methods.
|
||||
* @hide
|
||||
*
|
||||
*/
|
||||
public class DnsSdTxtRecord implements Parcelable {
|
||||
private static final byte mSeparator = '=';
|
||||
|
||||
private byte[] mData;
|
||||
|
||||
/** Constructs a new, empty TXT record. */
|
||||
public DnsSdTxtRecord() {
|
||||
mData = new byte[0];
|
||||
}
|
||||
|
||||
/** Constructs a new TXT record from a byte array in the standard format. */
|
||||
public DnsSdTxtRecord(byte[] data) {
|
||||
mData = (byte[]) data.clone();
|
||||
}
|
||||
|
||||
/** Copy constructor */
|
||||
public DnsSdTxtRecord(DnsSdTxtRecord src) {
|
||||
if (src != null && src.mData != null) {
|
||||
mData = (byte[]) src.mData.clone();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a key/value pair. Setting an existing key will replace its value.
|
||||
* @param key Must be ascii with no '='
|
||||
* @param value matching value to key
|
||||
*/
|
||||
public void set(String key, String value) {
|
||||
byte[] keyBytes;
|
||||
byte[] valBytes;
|
||||
int valLen;
|
||||
|
||||
if (value != null) {
|
||||
valBytes = value.getBytes();
|
||||
valLen = valBytes.length;
|
||||
} else {
|
||||
valBytes = null;
|
||||
valLen = 0;
|
||||
}
|
||||
|
||||
try {
|
||||
keyBytes = key.getBytes("US-ASCII");
|
||||
}
|
||||
catch (java.io.UnsupportedEncodingException e) {
|
||||
throw new IllegalArgumentException("key should be US-ASCII");
|
||||
}
|
||||
|
||||
for (int i = 0; i < keyBytes.length; i++) {
|
||||
if (keyBytes[i] == '=') {
|
||||
throw new IllegalArgumentException("= is not a valid character in key");
|
||||
}
|
||||
}
|
||||
|
||||
if (keyBytes.length + valLen >= 255) {
|
||||
throw new IllegalArgumentException("Key and Value length cannot exceed 255 bytes");
|
||||
}
|
||||
|
||||
int currentLoc = remove(key);
|
||||
if (currentLoc == -1)
|
||||
currentLoc = keyCount();
|
||||
|
||||
insert(keyBytes, valBytes, currentLoc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a value for a key
|
||||
*
|
||||
* @param key
|
||||
* @return The value associated with the key
|
||||
*/
|
||||
public String get(String key) {
|
||||
byte[] val = this.getValue(key);
|
||||
return val != null ? new String(val) : null;
|
||||
}
|
||||
|
||||
/** Remove a key/value pair. If found, returns the index or -1 if not found */
|
||||
public int remove(String key) {
|
||||
int avStart = 0;
|
||||
|
||||
for (int i=0; avStart < mData.length; i++) {
|
||||
int avLen = mData[avStart];
|
||||
if (key.length() <= avLen &&
|
||||
(key.length() == avLen || mData[avStart + key.length() + 1] == mSeparator)) {
|
||||
String s = new String(mData, avStart + 1, key.length());
|
||||
if (0 == key.compareToIgnoreCase(s)) {
|
||||
byte[] oldBytes = mData;
|
||||
mData = new byte[oldBytes.length - avLen - 1];
|
||||
System.arraycopy(oldBytes, 0, mData, 0, avStart);
|
||||
System.arraycopy(oldBytes, avStart + avLen + 1, mData, avStart,
|
||||
oldBytes.length - avStart - avLen - 1);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
avStart += (0xFF & (avLen + 1));
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** Return the count of keys */
|
||||
public int keyCount() {
|
||||
int count = 0, nextKey;
|
||||
for (nextKey = 0; nextKey < mData.length; count++) {
|
||||
nextKey += (0xFF & (mData[nextKey] + 1));
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/** Return true if key is present, false if not. */
|
||||
public boolean contains(String key) {
|
||||
String s = null;
|
||||
for (int i = 0; null != (s = this.getKey(i)); i++) {
|
||||
if (0 == key.compareToIgnoreCase(s)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Gets the size in bytes */
|
||||
public int size() {
|
||||
return mData.length;
|
||||
}
|
||||
|
||||
/* Gets the raw data in bytes */
|
||||
public byte[] getRawData() {
|
||||
return (byte[]) mData.clone();
|
||||
}
|
||||
|
||||
private void insert(byte[] keyBytes, byte[] value, int index) {
|
||||
byte[] oldBytes = mData;
|
||||
int valLen = (value != null) ? value.length : 0;
|
||||
int insertion = 0;
|
||||
int newLen, avLen;
|
||||
|
||||
for (int i = 0; i < index && insertion < mData.length; i++) {
|
||||
insertion += (0xFF & (mData[insertion] + 1));
|
||||
}
|
||||
|
||||
avLen = keyBytes.length + valLen + (value != null ? 1 : 0);
|
||||
newLen = avLen + oldBytes.length + 1;
|
||||
|
||||
mData = new byte[newLen];
|
||||
System.arraycopy(oldBytes, 0, mData, 0, insertion);
|
||||
int secondHalfLen = oldBytes.length - insertion;
|
||||
System.arraycopy(oldBytes, insertion, mData, newLen - secondHalfLen, secondHalfLen);
|
||||
mData[insertion] = (byte) avLen;
|
||||
System.arraycopy(keyBytes, 0, mData, insertion + 1, keyBytes.length);
|
||||
if (value != null) {
|
||||
mData[insertion + 1 + keyBytes.length] = mSeparator;
|
||||
System.arraycopy(value, 0, mData, insertion + keyBytes.length + 2, valLen);
|
||||
}
|
||||
}
|
||||
|
||||
/** Return a key in the TXT record by zero-based index. Returns null if index exceeds the total number of keys. */
|
||||
private String getKey(int index) {
|
||||
int avStart = 0;
|
||||
|
||||
for (int i=0; i < index && avStart < mData.length; i++) {
|
||||
avStart += mData[avStart] + 1;
|
||||
}
|
||||
|
||||
if (avStart < mData.length) {
|
||||
int avLen = mData[avStart];
|
||||
int aLen = 0;
|
||||
|
||||
for (aLen=0; aLen < avLen; aLen++) {
|
||||
if (mData[avStart + aLen + 1] == mSeparator) break;
|
||||
}
|
||||
return new String(mData, avStart + 1, aLen);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up a key in the TXT record by zero-based index and return its value.
|
||||
* Returns null if index exceeds the total number of keys.
|
||||
* Returns null if the key is present with no value.
|
||||
*/
|
||||
private byte[] getValue(int index) {
|
||||
int avStart = 0;
|
||||
byte[] value = null;
|
||||
|
||||
for (int i=0; i < index && avStart < mData.length; i++) {
|
||||
avStart += mData[avStart] + 1;
|
||||
}
|
||||
|
||||
if (avStart < mData.length) {
|
||||
int avLen = mData[avStart];
|
||||
int aLen = 0;
|
||||
|
||||
for (aLen=0; aLen < avLen; aLen++) {
|
||||
if (mData[avStart + aLen + 1] == mSeparator) {
|
||||
value = new byte[avLen - aLen - 1];
|
||||
System.arraycopy(mData, avStart + aLen + 2, value, 0, avLen - aLen - 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private String getValueAsString(int index) {
|
||||
byte[] value = this.getValue(index);
|
||||
return value != null ? new String(value) : null;
|
||||
}
|
||||
|
||||
private byte[] getValue(String forKey) {
|
||||
String s = null;
|
||||
int i;
|
||||
|
||||
for (i = 0; null != (s = this.getKey(i)); i++) {
|
||||
if (0 == forKey.compareToIgnoreCase(s)) {
|
||||
return this.getValue(i);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a string representation.
|
||||
* Example : {key1=value1},{key2=value2}..
|
||||
*
|
||||
* For a key say like "key3" with null value
|
||||
* {key1=value1},{key2=value2}{key3}
|
||||
*/
|
||||
public String toString() {
|
||||
String a, result = null;
|
||||
|
||||
for (int i = 0; null != (a = this.getKey(i)); i++) {
|
||||
String av = "{" + a;
|
||||
String val = this.getValueAsString(i);
|
||||
if (val != null)
|
||||
av += "=" + val + "}";
|
||||
else
|
||||
av += "}";
|
||||
if (result == null)
|
||||
result = av;
|
||||
else
|
||||
result = result + ", " + av;
|
||||
}
|
||||
return result != null ? result : "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof DnsSdTxtRecord)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DnsSdTxtRecord record = (DnsSdTxtRecord)o;
|
||||
return Arrays.equals(record.mData, mData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(mData);
|
||||
}
|
||||
|
||||
/** Implement the Parcelable interface */
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Implement the Parcelable interface */
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeByteArray(mData);
|
||||
}
|
||||
|
||||
/** Implement the Parcelable interface */
|
||||
public static final @android.annotation.NonNull Creator<DnsSdTxtRecord> CREATOR =
|
||||
new Creator<DnsSdTxtRecord>() {
|
||||
public DnsSdTxtRecord createFromParcel(Parcel in) {
|
||||
DnsSdTxtRecord info = new DnsSdTxtRecord();
|
||||
in.readByteArray(info.mData);
|
||||
return info;
|
||||
}
|
||||
|
||||
public DnsSdTxtRecord[] newArray(int size) {
|
||||
return new DnsSdTxtRecord[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
223
staticlibs/framework/com/android/net/module/util/IpRange.java
Normal file
223
staticlibs/framework/com/android/net/module/util/IpRange.java
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
/*
|
||||
* Copyright (C) 2020 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 com.android.net.module.util;
|
||||
|
||||
import static com.android.internal.annotations.VisibleForTesting.Visibility;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.net.IpPrefix;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Queue;
|
||||
|
||||
/**
|
||||
* This class represents an IP range, i.e., a contiguous block of IP addresses defined by a starting
|
||||
* and ending IP address. These addresses may not be power-of-two aligned.
|
||||
*
|
||||
* <p>Conversion to prefixes are deterministic, and will always return the same set of {@link
|
||||
* IpPrefix}(es). Ordering of IpPrefix instances is not guaranteed.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public final class IpRange {
|
||||
private static final int SIGNUM_POSITIVE = 1;
|
||||
|
||||
private final byte[] mStartAddr;
|
||||
private final byte[] mEndAddr;
|
||||
|
||||
public IpRange(@NonNull InetAddress startAddr, @NonNull InetAddress endAddr) {
|
||||
Objects.requireNonNull(startAddr, "startAddr must not be null");
|
||||
Objects.requireNonNull(endAddr, "endAddr must not be null");
|
||||
|
||||
if (!startAddr.getClass().equals(endAddr.getClass())) {
|
||||
throw new IllegalArgumentException("Invalid range: Address family mismatch");
|
||||
}
|
||||
if (addrToBigInteger(startAddr.getAddress()).compareTo(
|
||||
addrToBigInteger(endAddr.getAddress())) >= 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid range; start address must be before end address");
|
||||
}
|
||||
|
||||
mStartAddr = startAddr.getAddress();
|
||||
mEndAddr = endAddr.getAddress();
|
||||
}
|
||||
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
public IpRange(@NonNull IpPrefix prefix) {
|
||||
Objects.requireNonNull(prefix, "prefix must not be null");
|
||||
|
||||
// Use masked address from IpPrefix to zero out lower order bits.
|
||||
mStartAddr = prefix.getRawAddress();
|
||||
|
||||
// Set all non-prefix bits to max.
|
||||
mEndAddr = prefix.getRawAddress();
|
||||
for (int bitIndex = prefix.getPrefixLength(); bitIndex < 8 * mEndAddr.length; ++bitIndex) {
|
||||
mEndAddr[bitIndex / 8] |= (byte) (0x80 >> (bitIndex % 8));
|
||||
}
|
||||
}
|
||||
|
||||
private static InetAddress getAsInetAddress(byte[] address) {
|
||||
try {
|
||||
return InetAddress.getByAddress(address);
|
||||
} catch (UnknownHostException e) {
|
||||
// Cannot happen. InetAddress.getByAddress can only throw an exception if the byte
|
||||
// array is the wrong length, but are always generated from InetAddress(es).
|
||||
throw new IllegalArgumentException("Address is invalid");
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
public InetAddress getStartAddr() {
|
||||
return getAsInetAddress(mStartAddr);
|
||||
}
|
||||
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
public InetAddress getEndAddr() {
|
||||
return getAsInetAddress(mEndAddr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts this IP range to a list of IpPrefix instances.
|
||||
*
|
||||
* <p>This method outputs the IpPrefix instances for use in the routing architecture.
|
||||
*
|
||||
* <p>For example, the range 192.0.2.4 - 192.0.3.1 converts to the following prefixes:
|
||||
*
|
||||
* <ul>
|
||||
* <li>192.0.2.128/25
|
||||
* <li>192.0.2.64/26
|
||||
* <li>192.0.2.32/27
|
||||
* <li>192.0.2.16/28
|
||||
* <li>192.0.2.8/29
|
||||
* <li>192.0.2.4/30
|
||||
* <li>192.0.3.0/31
|
||||
* </ul>
|
||||
*/
|
||||
public List<IpPrefix> asIpPrefixes() {
|
||||
final boolean isIpv6 = (mStartAddr.length == 16);
|
||||
final List<IpPrefix> result = new ArrayList<>();
|
||||
final Queue<IpPrefix> workingSet = new LinkedList<>();
|
||||
|
||||
// Start with the any-address. Inet4/6Address.ANY requires compiling against
|
||||
// core-platform-api.
|
||||
workingSet.add(new IpPrefix(isIpv6 ? getAsInetAddress(new byte[16]) /* IPv6_ANY */
|
||||
: getAsInetAddress(new byte[4]) /* IPv4_ANY */, 0));
|
||||
|
||||
// While items are still in the queue, test and narrow to subsets.
|
||||
while (!workingSet.isEmpty()) {
|
||||
final IpPrefix workingPrefix = workingSet.poll();
|
||||
final IpRange workingRange = new IpRange(workingPrefix);
|
||||
|
||||
// If the other range is contained within, it's part of the output. Do not test subsets,
|
||||
// or we will end up with duplicates.
|
||||
if (containsRange(workingRange)) {
|
||||
result.add(workingPrefix);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If there is any overlap, split the working range into it's two subsets, and
|
||||
// reevaluate those.
|
||||
if (overlapsRange(workingRange)) {
|
||||
workingSet.addAll(getSubsetPrefixes(workingPrefix));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the two prefixes that comprise the given prefix.
|
||||
*
|
||||
* <p>For example, for the prefix 192.0.2.0/24, this will return the two prefixes that combined
|
||||
* make up the current prefix:
|
||||
*
|
||||
* <ul>
|
||||
* <li>192.0.2.0/25
|
||||
* <li>192.0.2.128/25
|
||||
* </ul>
|
||||
*/
|
||||
private static List<IpPrefix> getSubsetPrefixes(IpPrefix prefix) {
|
||||
final List<IpPrefix> result = new ArrayList<>();
|
||||
|
||||
final int currentPrefixLen = prefix.getPrefixLength();
|
||||
result.add(new IpPrefix(prefix.getAddress(), currentPrefixLen + 1));
|
||||
|
||||
final byte[] other = prefix.getRawAddress();
|
||||
other[currentPrefixLen / 8] =
|
||||
(byte) (other[currentPrefixLen / 8] ^ (0x80 >> (currentPrefixLen % 8)));
|
||||
result.add(new IpPrefix(getAsInetAddress(other), currentPrefixLen + 1));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the other IP range is contained within this one
|
||||
*
|
||||
* <p>Checks based on byte values. For other to be contained within this IP range, other's
|
||||
* starting address must be greater or equal to the current IpRange's starting address, and the
|
||||
* other's ending address must be less than or equal to the current IP range's ending address.
|
||||
*/
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
public boolean containsRange(IpRange other) {
|
||||
return addrToBigInteger(mStartAddr).compareTo(addrToBigInteger(other.mStartAddr)) <= 0
|
||||
&& addrToBigInteger(mEndAddr).compareTo(addrToBigInteger(other.mEndAddr)) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the other IP range overlaps with this one
|
||||
*
|
||||
* <p>Checks based on byte values. For there to be overlap, this IpRange's starting address must
|
||||
* be less than the other's ending address, and vice versa.
|
||||
*/
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
public boolean overlapsRange(IpRange other) {
|
||||
return addrToBigInteger(mStartAddr).compareTo(addrToBigInteger(other.mEndAddr)) <= 0
|
||||
&& addrToBigInteger(other.mStartAddr).compareTo(addrToBigInteger(mEndAddr)) <= 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mStartAddr, mEndAddr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof IpRange)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final IpRange other = (IpRange) obj;
|
||||
return Arrays.equals(mStartAddr, other.mStartAddr)
|
||||
&& Arrays.equals(mEndAddr, other.mEndAddr);
|
||||
}
|
||||
|
||||
/** Gets the InetAddress in BigInteger form */
|
||||
private static BigInteger addrToBigInteger(byte[] addr) {
|
||||
// Since addr.getAddress() returns network byte order (big-endian), it is compatible with
|
||||
// the BigInteger constructor (which assumes big-endian).
|
||||
return new BigInteger(SIGNUM_POSITIVE, addr);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,225 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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 com.android.net.module.util;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.net.LinkAddress;
|
||||
import android.net.LinkProperties;
|
||||
import android.net.RouteInfo;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Collection of link properties utilities.
|
||||
* @hide
|
||||
*/
|
||||
public final class LinkPropertiesUtils {
|
||||
|
||||
/**
|
||||
* @param <T> The type of data to compare.
|
||||
*/
|
||||
public static class CompareResult<T> {
|
||||
public final List<T> removed = new ArrayList<>();
|
||||
public final List<T> added = new ArrayList<>();
|
||||
|
||||
public CompareResult() {}
|
||||
|
||||
public CompareResult(@Nullable Collection<T> oldItems, @Nullable Collection<T> newItems) {
|
||||
if (oldItems != null) {
|
||||
removed.addAll(oldItems);
|
||||
}
|
||||
if (newItems != null) {
|
||||
for (T newItem : newItems) {
|
||||
if (!removed.remove(newItem)) {
|
||||
added.add(newItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "removed=[" + TextUtils.join(",", removed)
|
||||
+ "] added=[" + TextUtils.join(",", added)
|
||||
+ "]";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic class to compare two lists of items of type {@code T} whose properties can change.
|
||||
* The items to be compared must provide a way to calculate a corresponding key of type
|
||||
* {@code K} such that if (and only if) an old and a new item have the same key, then the new
|
||||
* item is an update of the old item. Both the old list and the new list may not contain more
|
||||
* than one item with the same key, and may not contain any null items.
|
||||
*
|
||||
* @param <K> A class that represents the key of the items to be compared.
|
||||
* @param <T> The class that represents the object to be compared.
|
||||
*/
|
||||
public static class CompareOrUpdateResult<K, T> {
|
||||
public final List<T> added = new ArrayList<>();
|
||||
public final List<T> removed = new ArrayList<>();
|
||||
public final List<T> updated = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Compares two lists of items.
|
||||
* @param oldItems the old list of items.
|
||||
* @param newItems the new list of items.
|
||||
* @param keyCalculator a {@link Function} that calculates an item's key.
|
||||
*/
|
||||
public CompareOrUpdateResult(Collection<T> oldItems, Collection<T> newItems,
|
||||
Function<T, K> keyCalculator) {
|
||||
HashMap<K, T> updateTracker = new HashMap<>();
|
||||
|
||||
if (oldItems != null) {
|
||||
for (T oldItem : oldItems) {
|
||||
updateTracker.put(keyCalculator.apply(oldItem), oldItem);
|
||||
}
|
||||
}
|
||||
|
||||
if (newItems != null) {
|
||||
for (T newItem : newItems) {
|
||||
T oldItem = updateTracker.remove(keyCalculator.apply(newItem));
|
||||
if (oldItem != null) {
|
||||
if (!oldItem.equals(newItem)) {
|
||||
// Update of existing item.
|
||||
updated.add(newItem);
|
||||
}
|
||||
} else {
|
||||
// New item.
|
||||
added.add(newItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
removed.addAll(updateTracker.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "removed=[" + TextUtils.join(",", removed)
|
||||
+ "] added=[" + TextUtils.join(",", added)
|
||||
+ "] updated=[" + TextUtils.join(",", updated)
|
||||
+ "]";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the addresses in {@code left} LinkProperties with {@code right}
|
||||
* LinkProperties, examining only addresses on the base link.
|
||||
*
|
||||
* @param left A LinkProperties with the old list of addresses.
|
||||
* @param right A LinkProperties with the new list of addresses.
|
||||
* @return the differences between the addresses.
|
||||
*/
|
||||
public static @NonNull CompareResult<LinkAddress> compareAddresses(
|
||||
@Nullable LinkProperties left, @Nullable LinkProperties right) {
|
||||
/*
|
||||
* Duplicate the LinkAddresses into removed, we will be removing
|
||||
* address which are common between mLinkAddresses and target
|
||||
* leaving the addresses that are different. And address which
|
||||
* are in target but not in mLinkAddresses are placed in the
|
||||
* addedAddresses.
|
||||
*/
|
||||
return new CompareResult<>(left != null ? left.getLinkAddresses() : null,
|
||||
right != null ? right.getLinkAddresses() : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares {@code left} {@code LinkProperties} interface addresses against the {@code right}.
|
||||
*
|
||||
* @param left A LinkProperties.
|
||||
* @param right LinkProperties to be compared with {@code left}.
|
||||
* @return {@code true} if both are identical, {@code false} otherwise.
|
||||
*/
|
||||
public static boolean isIdenticalAddresses(@NonNull LinkProperties left,
|
||||
@NonNull LinkProperties right) {
|
||||
final Collection<InetAddress> leftAddresses = left.getAddresses();
|
||||
final Collection<InetAddress> rightAddresses = right.getAddresses();
|
||||
return (leftAddresses.size() == rightAddresses.size())
|
||||
? leftAddresses.containsAll(rightAddresses) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares {@code left} {@code LinkProperties} DNS addresses against the {@code right}.
|
||||
*
|
||||
* @param left A LinkProperties.
|
||||
* @param right A LinkProperties to be compared with {@code left}.
|
||||
* @return {@code true} if both are identical, {@code false} otherwise.
|
||||
*/
|
||||
public static boolean isIdenticalDnses(@NonNull LinkProperties left,
|
||||
@NonNull LinkProperties right) {
|
||||
final Collection<InetAddress> leftDnses = left.getDnsServers();
|
||||
final Collection<InetAddress> rightDnses = right.getDnsServers();
|
||||
|
||||
final String leftDomains = left.getDomains();
|
||||
final String rightDomains = right.getDomains();
|
||||
if (leftDomains == null) {
|
||||
if (rightDomains != null) return false;
|
||||
} else {
|
||||
if (!leftDomains.equals(rightDomains)) return false;
|
||||
}
|
||||
return (leftDnses.size() == rightDnses.size())
|
||||
? leftDnses.containsAll(rightDnses) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares {@code left} {@code LinkProperties} HttpProxy against the {@code right}.
|
||||
*
|
||||
* @param left A LinkProperties.
|
||||
* @param right A LinkProperties to be compared with {@code left}.
|
||||
* @return {@code true} if both are identical, {@code false} otherwise.
|
||||
*/
|
||||
public static boolean isIdenticalHttpProxy(@NonNull LinkProperties left,
|
||||
@NonNull LinkProperties right) {
|
||||
return Objects.equals(left.getHttpProxy(), right.getHttpProxy());
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares {@code left} {@code LinkProperties} interface name against the {@code right}.
|
||||
*
|
||||
* @param left A LinkProperties.
|
||||
* @param right A LinkProperties to be compared with {@code left}.
|
||||
* @return {@code true} if both are identical, {@code false} otherwise.
|
||||
*/
|
||||
public static boolean isIdenticalInterfaceName(@NonNull LinkProperties left,
|
||||
@NonNull LinkProperties right) {
|
||||
return TextUtils.equals(left.getInterfaceName(), right.getInterfaceName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares {@code left} {@code LinkProperties} Routes against the {@code right}.
|
||||
*
|
||||
* @param left A LinkProperties.
|
||||
* @param right A LinkProperties to be compared with {@code left}.
|
||||
* @return {@code true} if both are identical, {@code false} otherwise.
|
||||
*/
|
||||
public static boolean isIdenticalRoutes(@NonNull LinkProperties left,
|
||||
@NonNull LinkProperties right) {
|
||||
final Collection<RouteInfo> leftRoutes = left.getRoutes();
|
||||
final Collection<RouteInfo> rightRoutes = right.getRoutes();
|
||||
return (leftRoutes.size() == rightRoutes.size())
|
||||
? leftRoutes.containsAll(rightRoutes) : false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* Copyright 2019 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 com.android.net.module.util;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.net.MacAddress;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Collection of MAC address utilities.
|
||||
* @hide
|
||||
*/
|
||||
public final class MacAddressUtils {
|
||||
|
||||
private static final long VALID_LONG_MASK = (1L << 48) - 1;
|
||||
private static final long LOCALLY_ASSIGNED_MASK = longAddrFromByteAddr(
|
||||
MacAddress.fromString("2:0:0:0:0:0").toByteArray());
|
||||
private static final long MULTICAST_MASK = longAddrFromByteAddr(
|
||||
MacAddress.fromString("1:0:0:0:0:0").toByteArray());
|
||||
private static final long OUI_MASK = longAddrFromByteAddr(
|
||||
MacAddress.fromString("ff:ff:ff:0:0:0").toByteArray());
|
||||
private static final long NIC_MASK = longAddrFromByteAddr(
|
||||
MacAddress.fromString("0:0:0:ff:ff:ff").toByteArray());
|
||||
// Matches WifiInfo.DEFAULT_MAC_ADDRESS
|
||||
private static final String DEFAULT_MAC_ADDRESS = "02:00:00:00:00:00";
|
||||
private static final int ETHER_ADDR_LEN = 6;
|
||||
|
||||
/**
|
||||
* @return true if this MacAddress is a multicast address.
|
||||
*/
|
||||
public static boolean isMulticastAddress(@NonNull MacAddress address) {
|
||||
return (longAddrFromByteAddr(address.toByteArray()) & MULTICAST_MASK) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a generated MAC address whose 46 bits, excluding the locally assigned bit and the
|
||||
* unicast bit, 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, unicast MacAddress.
|
||||
*/
|
||||
public static @NonNull MacAddress createRandomUnicastAddress() {
|
||||
return createRandomUnicastAddress(null, new SecureRandom());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* If base == null then the OUI will also be randomized.
|
||||
* @param r a standard Java Random object used for generating the random address.
|
||||
* @return a random locally assigned MacAddress.
|
||||
*/
|
||||
public static @NonNull MacAddress createRandomUnicastAddress(@Nullable MacAddress base,
|
||||
@NonNull Random r) {
|
||||
long addr;
|
||||
|
||||
if (base == null) {
|
||||
addr = r.nextLong() & VALID_LONG_MASK;
|
||||
} else {
|
||||
addr = (longAddrFromByteAddr(base.toByteArray()) & OUI_MASK)
|
||||
| (NIC_MASK & r.nextLong());
|
||||
}
|
||||
addr |= LOCALLY_ASSIGNED_MASK;
|
||||
addr &= ~MULTICAST_MASK;
|
||||
MacAddress mac = MacAddress.fromBytes(byteAddrFromLongAddr(addr));
|
||||
if (mac.equals(DEFAULT_MAC_ADDRESS)) {
|
||||
return createRandomUnicastAddress(base, r);
|
||||
}
|
||||
return mac;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a byte address to long address.
|
||||
*/
|
||||
public static long longAddrFromByteAddr(byte[] addr) {
|
||||
Objects.requireNonNull(addr);
|
||||
if (!isMacAddress(addr)) {
|
||||
throw new IllegalArgumentException(
|
||||
Arrays.toString(addr) + " was not a valid MAC address");
|
||||
}
|
||||
long longAddr = 0;
|
||||
for (byte b : addr) {
|
||||
final int uint8Byte = b & 0xff;
|
||||
longAddr = (longAddr << 8) + uint8Byte;
|
||||
}
|
||||
return longAddr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a long address to byte address.
|
||||
*/
|
||||
public 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given byte array is a 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.
|
||||
*
|
||||
*/
|
||||
public static boolean isMacAddress(byte[] addr) {
|
||||
return addr != null && addr.length == ETHER_ADDR_LEN;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright 2019 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 com.android.net.module.util;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.net.RouteInfo;
|
||||
|
||||
import java.net.Inet4Address;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Collection of network common utilities.
|
||||
* @hide
|
||||
*/
|
||||
public final class NetUtils {
|
||||
|
||||
/**
|
||||
* Check if IP address type is consistent between two InetAddress.
|
||||
* @return true if both are the same type. False otherwise.
|
||||
*/
|
||||
public static boolean addressTypeMatches(@NonNull InetAddress left,
|
||||
@NonNull InetAddress right) {
|
||||
return (((left instanceof Inet4Address) && (right instanceof Inet4Address))
|
||||
|| ((left instanceof Inet6Address) && (right instanceof Inet6Address)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the route from a Collection of routes that best matches a given address.
|
||||
* May return null if no routes are applicable.
|
||||
* @param routes a Collection of RouteInfos to chose from
|
||||
* @param dest the InetAddress your trying to get to
|
||||
* @return the RouteInfo from the Collection that best fits the given address
|
||||
*/
|
||||
@Nullable
|
||||
public static RouteInfo selectBestRoute(@Nullable Collection<RouteInfo> routes,
|
||||
@Nullable InetAddress dest) {
|
||||
if ((routes == null) || (dest == null)) return null;
|
||||
|
||||
RouteInfo bestRoute = null;
|
||||
// pick a longest prefix match under same address type
|
||||
for (RouteInfo route : routes) {
|
||||
if (addressTypeMatches(route.getDestination().getAddress(), dest)) {
|
||||
if ((bestRoute != null)
|
||||
&& (bestRoute.getDestination().getPrefixLength()
|
||||
>= route.getDestination().getPrefixLength())) {
|
||||
continue;
|
||||
}
|
||||
if (route.matches(dest)) bestRoute = route;
|
||||
}
|
||||
}
|
||||
return bestRoute;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,2 @@
|
|||
# TODO: move the classes to the target package in java
|
||||
rule android.net.util.IpRange* com.android.net.module.util.IpRange@1
|
||||
rule android.net.util.MacAddressUtils* com.android.net.module.util.MacAddressUtils@1
|
||||
rule android.net.util.LinkPropertiesUtils* com.android.net.module.util.LinkPropertiesUtils@1
|
||||
rule android.net.util.NetUtils* com.android.net.module.util.NetUtils@1
|
||||
rule android.net.util.nsd.** com.android.net.module.util.nsd.@1
|
||||
rule android.annotation.** com.android.net.module.annotation.@1
|
||||
rule com.android.internal.annotations.** com.android.net.module.annotation.@1
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ android_library {
|
|||
name: "NetworkStaticLibTestsLib",
|
||||
srcs: ["src/**/*.java","src/**/*.kt"],
|
||||
min_sdk_version: "29",
|
||||
jarjar_rules: "jarjar-rules.txt",
|
||||
static_libs: [
|
||||
"net-utils-framework-common",
|
||||
"androidx.test.rules",
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.frameworks.libnet.tests">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application>
|
||||
<uses-library android:name="android.test.runner" />
|
||||
</application>
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
# TODO: move the classes to the target package in java
|
||||
rule android.net.util.IpRange* com.android.net.module.util.IpRange@1
|
||||
rule android.net.util.MacAddressUtils* com.android.net.module.util.MacAddressUtils@1
|
||||
rule android.net.util.LinkPropertiesUtils* com.android.net.module.util.LinkPropertiesUtils@1
|
||||
rule android.net.util.NetUtils* com.android.net.module.util.NetUtils@1
|
||||
rule android.net.util.nsd.** com.android.net.module.util.nsd.@1
|
||||
|
|
@ -28,6 +28,8 @@ import android.net.IpPrefix;
|
|||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.net.module.util.IpRange;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
|
|
|
|||
|
|
@ -26,11 +26,13 @@ import android.net.LinkAddress;
|
|||
import android.net.LinkProperties;
|
||||
import android.net.ProxyInfo;
|
||||
import android.net.RouteInfo;
|
||||
import android.net.util.LinkPropertiesUtils.CompareOrUpdateResult;
|
||||
import android.net.util.LinkPropertiesUtils.CompareResult;
|
||||
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.net.module.util.LinkPropertiesUtils;
|
||||
import com.android.net.module.util.LinkPropertiesUtils.CompareOrUpdateResult;
|
||||
import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ import android.net.MacAddress;
|
|||
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.net.module.util.MacAddressUtils;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@ import android.net.RouteInfo;
|
|||
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.net.module.util.NetUtils;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
package com.android.net.module.util;
|
||||
|
||||
import static android.net.util.PacketReader.DEFAULT_RECV_BUF_SIZE;
|
||||
import static android.system.OsConstants.AF_INET6;
|
||||
import static android.system.OsConstants.IPPROTO_UDP;
|
||||
import static android.system.OsConstants.SOCK_DGRAM;
|
||||
|
|
@ -24,14 +23,13 @@ import static android.system.OsConstants.SOCK_NONBLOCK;
|
|||
import static android.system.OsConstants.SOL_SOCKET;
|
||||
import static android.system.OsConstants.SO_SNDTIMEO;
|
||||
|
||||
import static com.android.net.module.util.PacketReader.DEFAULT_RECV_BUF_SIZE;
|
||||
import static com.android.testutils.MiscAsserts.assertThrows;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import android.net.util.PacketReader;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.system.ErrnoException;
|
||||
|
|
@ -93,8 +91,7 @@ public class PacketReaderTest {
|
|||
Os.setsockoptTimeval(s, SOL_SOCKET, SO_SNDTIMEO, TIMEO);
|
||||
} catch (ErrnoException | SocketException e) {
|
||||
closeFd(s);
|
||||
fail();
|
||||
return null;
|
||||
throw new RuntimeException("Failed to create FD", e);
|
||||
}
|
||||
|
||||
mLocalSocket = s;
|
||||
|
|
|
|||
Loading…
Reference in a new issue