/* * Copyright (C) 2015 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.netlink; import android.system.ErrnoException; import android.system.NetlinkSocketAddress; import android.system.Os; import android.system.OsConstants; import android.system.StructTimeval; import android.util.Log; import libcore.io.IoUtils; import libcore.io.Libcore; import java.io.Closeable; import java.io.FileDescriptor; import java.io.InterruptedIOException; import java.net.SocketAddress; import java.net.SocketException; import java.nio.ByteBuffer; import java.nio.ByteOrder; /** * NetlinkSocket * * A small wrapper class to assist with AF_NETLINK socket operations. * * @hide */ public class NetlinkSocket implements Closeable { private static final String TAG = "NetlinkSocket"; private static final int SOCKET_RECV_BUFSIZE = 64 * 1024; private static final int DEFAULT_RECV_BUFSIZE = 8 * 1024; final private FileDescriptor mDescriptor; private NetlinkSocketAddress mAddr; private long mLastRecvTimeoutMs; private long mLastSendTimeoutMs; public NetlinkSocket(int nlProto) throws ErrnoException { mDescriptor = Os.socket( OsConstants.AF_NETLINK, OsConstants.SOCK_DGRAM, nlProto); Libcore.os.setsockoptInt( mDescriptor, OsConstants.SOL_SOCKET, OsConstants.SO_RCVBUF, SOCKET_RECV_BUFSIZE); } public NetlinkSocketAddress getLocalAddress() throws ErrnoException { return (NetlinkSocketAddress) Os.getsockname(mDescriptor); } public void bind(NetlinkSocketAddress localAddr) throws ErrnoException, SocketException { Os.bind(mDescriptor, (SocketAddress)localAddr); } public void connectTo(NetlinkSocketAddress peerAddr) throws ErrnoException, SocketException { Os.connect(mDescriptor, (SocketAddress) peerAddr); } public void connectToKernel() throws ErrnoException, SocketException { connectTo(new NetlinkSocketAddress(0, 0)); } /** * Wait indefinitely (or until underlying socket error) for a * netlink message of at most DEFAULT_RECV_BUFSIZE size. */ public ByteBuffer recvMessage() throws ErrnoException, InterruptedIOException { return recvMessage(DEFAULT_RECV_BUFSIZE, 0); } /** * Wait up to |timeoutMs| (or until underlying socket error) for a * netlink message of at most DEFAULT_RECV_BUFSIZE size. */ public ByteBuffer recvMessage(long timeoutMs) throws ErrnoException, InterruptedIOException { return recvMessage(DEFAULT_RECV_BUFSIZE, timeoutMs); } private void checkTimeout(long timeoutMs) { if (timeoutMs < 0) { throw new IllegalArgumentException("Negative timeouts not permitted"); } } /** * Wait up to |timeoutMs| (or until underlying socket error) for a * netlink message of at most |bufsize| size. * * Multi-threaded calls with different timeouts will cause unexpected results. */ public ByteBuffer recvMessage(int bufsize, long timeoutMs) throws ErrnoException, IllegalArgumentException, InterruptedIOException { checkTimeout(timeoutMs); synchronized (mDescriptor) { if (mLastRecvTimeoutMs != timeoutMs) { Os.setsockoptTimeval(mDescriptor, OsConstants.SOL_SOCKET, OsConstants.SO_RCVTIMEO, StructTimeval.fromMillis(timeoutMs)); mLastRecvTimeoutMs = timeoutMs; } } ByteBuffer byteBuffer = ByteBuffer.allocate(bufsize); int length = Os.read(mDescriptor, byteBuffer); if (length == bufsize) { Log.w(TAG, "maximum read"); } byteBuffer.position(0); byteBuffer.limit(length); byteBuffer.order(ByteOrder.nativeOrder()); return byteBuffer; } /** * Send a message to a peer to which this socket has previously connected. * * This blocks until completion or an error occurs. */ public boolean sendMessage(byte[] bytes, int offset, int count) throws ErrnoException, InterruptedIOException { return sendMessage(bytes, offset, count, 0); } /** * Send a message to a peer to which this socket has previously connected, * waiting at most |timeoutMs| milliseconds for the send to complete. * * Multi-threaded calls with different timeouts will cause unexpected results. */ public boolean sendMessage(byte[] bytes, int offset, int count, long timeoutMs) throws ErrnoException, IllegalArgumentException, InterruptedIOException { checkTimeout(timeoutMs); synchronized (mDescriptor) { if (mLastSendTimeoutMs != timeoutMs) { Os.setsockoptTimeval(mDescriptor, OsConstants.SOL_SOCKET, OsConstants.SO_SNDTIMEO, StructTimeval.fromMillis(timeoutMs)); mLastSendTimeoutMs = timeoutMs; } } return (count == Os.write(mDescriptor, bytes, offset, count)); } @Override public void close() { IoUtils.closeQuietly(mDescriptor); } }