1 /* 2 * Copyright (C) 2020 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 17 package com.android.testutils; 18 19 import android.net.util.PacketReader; 20 import android.os.Handler; 21 22 import androidx.annotation.NonNull; 23 import androidx.annotation.Nullable; 24 25 import java.io.FileDescriptor; 26 import java.io.FileOutputStream; 27 import java.io.IOException; 28 import java.nio.ByteBuffer; 29 import java.util.Arrays; 30 import java.util.function.Predicate; 31 32 import kotlin.Lazy; 33 import kotlin.LazyKt; 34 35 public class TapPacketReader extends PacketReader { 36 private final FileDescriptor mTapFd; 37 private final ArrayTrackRecord<byte[]> mReceivedPackets = new ArrayTrackRecord<>(); 38 private final Lazy<ArrayTrackRecord<byte[]>.ReadHead> mReadHead = 39 LazyKt.lazy(mReceivedPackets::newReadHead); 40 TapPacketReader(Handler h, FileDescriptor tapFd, int maxPacketSize)41 public TapPacketReader(Handler h, FileDescriptor tapFd, int maxPacketSize) { 42 super(h, maxPacketSize); 43 mTapFd = tapFd; 44 } 45 46 @Override createFd()47 protected FileDescriptor createFd() { 48 return mTapFd; 49 } 50 51 @Override handlePacket(byte[] recvbuf, int length)52 protected void handlePacket(byte[] recvbuf, int length) { 53 final byte[] newPacket = Arrays.copyOf(recvbuf, length); 54 if (!mReceivedPackets.add(newPacket)) { 55 throw new AssertionError("More than " + Integer.MAX_VALUE + " packets outstanding!"); 56 } 57 } 58 59 /** 60 * Get the next packet that was received on the interface. 61 */ 62 @Nullable popPacket(long timeoutMs)63 public byte[] popPacket(long timeoutMs) { 64 return mReadHead.getValue().poll(timeoutMs, packet -> true); 65 } 66 67 /** 68 * Get the next packet that was received on the interface and matches the specified filter. 69 */ 70 @Nullable popPacket(long timeoutMs, @NonNull Predicate<byte[]> filter)71 public byte[] popPacket(long timeoutMs, @NonNull Predicate<byte[]> filter) { 72 return mReadHead.getValue().poll(timeoutMs, filter::test); 73 } 74 sendResponse(final ByteBuffer packet)75 public void sendResponse(final ByteBuffer packet) throws IOException { 76 try (FileOutputStream out = new FileOutputStream(mTapFd)) { 77 byte[] packetBytes = new byte[packet.limit()]; 78 packet.get(packetBytes); 79 packet.flip(); // So we can reuse it in the future. 80 out.write(packetBytes); 81 } 82 } 83 } 84