1/*
2 * Copyright (C) 2016 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 libcore.dalvik.system;
18
19import android.system.ErrnoException;
20import android.system.StructStat;
21import dalvik.system.SocketTagger;
22import junit.framework.TestCase;
23
24import java.io.FileDescriptor;
25import java.net.DatagramSocket;
26import java.net.Socket;
27import java.net.SocketException;
28import java.nio.channels.DatagramChannel;
29import java.nio.channels.ServerSocketChannel;
30import java.nio.channels.SocketChannel;
31import java.util.Collections;
32import java.util.HashMap;
33import java.util.Map;
34
35public class SocketTaggingTest extends TestCase {
36    static final class StatAndDescriptor {
37        final int fd;
38        final StructStat stat;
39
40        StatAndDescriptor(FileDescriptor fd) {
41            this.fd = fd.getInt$();
42            this.stat = fstat(fd);
43        }
44
45        @Override
46        public String toString() {
47            return "[fd=" + fd + ", stat=" + stat + "]";
48        }
49    }
50
51    static class RecordingSocketTagger extends SocketTagger {
52        private final Map<Integer, StatAndDescriptor> liveDescriptors = new HashMap<>();
53
54        @Override
55        public void tag(FileDescriptor socketDescriptor) throws SocketException {
56            liveDescriptors.put(socketDescriptor.getInt$(),
57                    new StatAndDescriptor(socketDescriptor));
58        }
59
60        @Override
61        public void untag(FileDescriptor socketDescriptor) throws SocketException {
62            StatAndDescriptor existing = liveDescriptors.remove(socketDescriptor.getInt$());
63
64            // We compare the current description of the descriptor with the description
65            // we used to tag with and make sure they describe the same file. This helps test
66            // whether we untag the socket at the "right" time.
67            StructStat current = fstat(socketDescriptor);
68            assertEquals(existing.stat.st_dev, current.st_dev);
69            assertEquals(existing.stat.st_ino, current.st_ino);
70        }
71
72        public Map<Integer, StatAndDescriptor> getLiveDescriptors() {
73            return liveDescriptors;
74        }
75    }
76
77    private RecordingSocketTagger tagger;
78    private SocketTagger original;
79
80    private ServerSocketChannel server;
81
82    @Override
83    public void setUp() throws Exception {
84        server = ServerSocketChannel.open();
85        server.configureBlocking(false);
86        server.bind(null);
87
88        original = SocketTagger.get();
89        tagger = new RecordingSocketTagger();
90        SocketTagger.set(tagger);
91    }
92
93    @Override
94    public void tearDown() {
95        SocketTagger.set(original);
96    }
97
98    public void testSocketChannel() throws Exception {
99        SocketChannel sc = SocketChannel.open();
100        sc.connect(server.getLocalAddress());
101        assertEquals(1, tagger.getLiveDescriptors().size());
102
103        sc.close();
104
105        assertEquals(Collections.EMPTY_MAP, tagger.getLiveDescriptors());
106    }
107
108    public void testServerSocketChannel() throws Exception {
109        ServerSocketChannel ssc = ServerSocketChannel.open();
110        ssc.bind(null);
111        assertEquals(1, tagger.getLiveDescriptors().size());
112
113        ssc.close();
114
115        assertEquals(Collections.EMPTY_MAP, tagger.getLiveDescriptors());
116    }
117
118    public void testDatagramChannel() throws Exception {
119        DatagramChannel dc = DatagramChannel.open();
120        dc.connect(server.getLocalAddress());
121        assertEquals(1, tagger.getLiveDescriptors().size());
122
123        dc.close();
124
125        assertEquals(Collections.EMPTY_MAP, tagger.getLiveDescriptors());
126    }
127
128    public void testSocket() throws Exception {
129        Socket s = new Socket();
130        s.connect(server.getLocalAddress());
131        assertEquals(1, tagger.getLiveDescriptors().size());
132
133        s.close();
134
135        assertEquals(Collections.EMPTY_MAP, tagger.getLiveDescriptors());
136    }
137
138    public void testDatagramSocket() throws Exception {
139        DatagramSocket d = new DatagramSocket();
140        d.connect(server.getLocalAddress());
141        assertEquals(1, tagger.getLiveDescriptors().size());
142
143        d.close();
144
145        assertEquals(Collections.EMPTY_MAP, tagger.getLiveDescriptors());
146    }
147
148    private static StructStat fstat(FileDescriptor fd) {
149        try {
150            return android.system.Os.fstat(fd);
151        } catch (ErrnoException e) {
152            throw new AssertionError(e);
153        }
154    }
155}
156