DnsPinger.java revision a7bc1135c270fd4a84ab7ad45b7194e9b580300e
1bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy/*
2bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy * Copyright (C) 2011 The Android Open Source Project
3bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy *
4bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy * Licensed under the Apache License, Version 2.0 (the "License");
5bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy * you may not use this file except in compliance with the License.
6bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy * You may obtain a copy of the License at
7bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy *
8bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy *      http://www.apache.org/licenses/LICENSE-2.0
9bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy *
10bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy * Unless required by applicable law or agreed to in writing, software
11bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy * distributed under the License is distributed on an "AS IS" BASIS,
12bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy * See the License for the specific language governing permissions and
14bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy * limitations under the License.
15bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy */
16bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
17a7bc1135c270fd4a84ab7ad45b7194e9b580300eIsaac Levypackage android.net;
18bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
19bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levyimport android.content.Context;
20bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levyimport android.net.ConnectivityManager;
21bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levyimport android.net.LinkProperties;
223541ce04ddb1b559ac9e79b5067c93b910f22955Isaac Levyimport android.net.NetworkUtils;
23bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levyimport android.os.SystemClock;
243541ce04ddb1b559ac9e79b5067c93b910f22955Isaac Levyimport android.provider.Settings;
25bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levyimport android.util.Slog;
26bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
27bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levyimport java.net.DatagramPacket;
28bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levyimport java.net.DatagramSocket;
29bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levyimport java.net.InetAddress;
303ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levyimport java.net.NetworkInterface;
31bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levyimport java.net.SocketTimeoutException;
32bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levyimport java.util.Collection;
33bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levyimport java.util.Random;
34bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
35bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy/**
36bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy * Performs a simple DNS "ping" by sending a "server status" query packet to the
37bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy * DNS server. As long as the server replies, we consider it a success.
38bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy * <p>
39bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy * We do not use a simple hostname lookup because that could be cached and the
40bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy * API may not differentiate between a time out and a failure lookup (which we
41bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy * really care about).
42bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy * <p>
433541ce04ddb1b559ac9e79b5067c93b910f22955Isaac Levy * TODO : More general API. Socket does not bind to specified connection type
44bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy * TODO : Choice of DNS query location - current looks up www.android.com
45bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy *
46bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy * @hide
47bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy */
48bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levypublic final class DnsPinger {
49bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy    private static final boolean V = true;
50bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
51bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy    /** Number of bytes for the query */
523ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levy    private static final int DNS_QUERY_BASE_SIZE = 32;
53bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
54bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy    /** The DNS port */
55bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy    private static final int DNS_PORT = 53;
56bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
57bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy    /** Used to generate IDs */
58bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy    private static Random sRandom = new Random();
59bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
60bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy    private ConnectivityManager mConnectivityManager = null;
61bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy    private Context mContext;
62b1ef292b3d0c2b4b4c77bb7b442df8e73d1fbb5eIsaac Levy    private int mConnectionType;
633541ce04ddb1b559ac9e79b5067c93b910f22955Isaac Levy    private InetAddress mDefaultDns;
64bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
65bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy    private String TAG;
66bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
67b1ef292b3d0c2b4b4c77bb7b442df8e73d1fbb5eIsaac Levy    /**
683541ce04ddb1b559ac9e79b5067c93b910f22955Isaac Levy     * @param connectionType The connection type from {@link ConnectivityManager}
69b1ef292b3d0c2b4b4c77bb7b442df8e73d1fbb5eIsaac Levy     */
70b1ef292b3d0c2b4b4c77bb7b442df8e73d1fbb5eIsaac Levy    public DnsPinger(String TAG, Context context, int connectionType) {
71bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        mContext = context;
72b1ef292b3d0c2b4b4c77bb7b442df8e73d1fbb5eIsaac Levy        mConnectionType = connectionType;
733541ce04ddb1b559ac9e79b5067c93b910f22955Isaac Levy        if (!ConnectivityManager.isNetworkTypeValid(connectionType)) {
743541ce04ddb1b559ac9e79b5067c93b910f22955Isaac Levy            Slog.e(TAG, "Invalid connectionType in constructor: " + connectionType);
753541ce04ddb1b559ac9e79b5067c93b910f22955Isaac Levy        }
76bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        this.TAG = TAG;
773541ce04ddb1b559ac9e79b5067c93b910f22955Isaac Levy
783541ce04ddb1b559ac9e79b5067c93b910f22955Isaac Levy        mDefaultDns = getDefaultDns();
79bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy    }
80bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
81bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy    /**
823541ce04ddb1b559ac9e79b5067c93b910f22955Isaac Levy     * @return The first DNS in the link properties of the specified connection
833541ce04ddb1b559ac9e79b5067c93b910f22955Isaac Levy     *         type or the default system DNS if the link properties has null
843541ce04ddb1b559ac9e79b5067c93b910f22955Isaac Levy     *         dns set. Should not be null.
85bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy     */
86bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy    public InetAddress getDns() {
873ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levy        LinkProperties curLinkProps = getCurrentLinkProperties();
883541ce04ddb1b559ac9e79b5067c93b910f22955Isaac Levy        if (curLinkProps == null) {
893541ce04ddb1b559ac9e79b5067c93b910f22955Isaac Levy            Slog.e(TAG, "getCurLinkProperties:: LP for type" + mConnectionType + " is null!");
903541ce04ddb1b559ac9e79b5067c93b910f22955Isaac Levy            return mDefaultDns;
913541ce04ddb1b559ac9e79b5067c93b910f22955Isaac Levy        }
923541ce04ddb1b559ac9e79b5067c93b910f22955Isaac Levy
933541ce04ddb1b559ac9e79b5067c93b910f22955Isaac Levy        Collection<InetAddress> dnses = curLinkProps.getDnses();
943541ce04ddb1b559ac9e79b5067c93b910f22955Isaac Levy        if (dnses == null || dnses.size() == 0) {
953541ce04ddb1b559ac9e79b5067c93b910f22955Isaac Levy            Slog.v(TAG, "getDns::LinkProps has null dns - returning default");
963541ce04ddb1b559ac9e79b5067c93b910f22955Isaac Levy            return mDefaultDns;
973541ce04ddb1b559ac9e79b5067c93b910f22955Isaac Levy        }
98bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
99bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        return dnses.iterator().next();
100bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy    }
101bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
1023ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levy    private LinkProperties getCurrentLinkProperties() {
1033ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levy        if (mConnectivityManager == null) {
1043ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levy            mConnectivityManager = (ConnectivityManager) mContext.getSystemService(
1053ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levy                    Context.CONNECTIVITY_SERVICE);
1063ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levy        }
1073ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levy
1083ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levy        return mConnectivityManager.getLinkProperties(mConnectionType);
1093ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levy    }
1103ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levy
1113541ce04ddb1b559ac9e79b5067c93b910f22955Isaac Levy    private InetAddress getDefaultDns() {
1123541ce04ddb1b559ac9e79b5067c93b910f22955Isaac Levy        String dns = Settings.Secure.getString(mContext.getContentResolver(),
1133541ce04ddb1b559ac9e79b5067c93b910f22955Isaac Levy                Settings.Secure.DEFAULT_DNS_SERVER);
1143541ce04ddb1b559ac9e79b5067c93b910f22955Isaac Levy        if (dns == null || dns.length() == 0) {
1153541ce04ddb1b559ac9e79b5067c93b910f22955Isaac Levy            dns = mContext.getResources().getString(
1163541ce04ddb1b559ac9e79b5067c93b910f22955Isaac Levy                    com.android.internal.R.string.config_default_dns_server);
1173541ce04ddb1b559ac9e79b5067c93b910f22955Isaac Levy        }
1183541ce04ddb1b559ac9e79b5067c93b910f22955Isaac Levy        try {
1193541ce04ddb1b559ac9e79b5067c93b910f22955Isaac Levy            return NetworkUtils.numericToInetAddress(dns);
1203541ce04ddb1b559ac9e79b5067c93b910f22955Isaac Levy        } catch (IllegalArgumentException e) {
1213541ce04ddb1b559ac9e79b5067c93b910f22955Isaac Levy            Slog.w(TAG, "getDefaultDns::malformed default dns address");
1223541ce04ddb1b559ac9e79b5067c93b910f22955Isaac Levy            return null;
123b1ef292b3d0c2b4b4c77bb7b442df8e73d1fbb5eIsaac Levy        }
124b1ef292b3d0c2b4b4c77bb7b442df8e73d1fbb5eIsaac Levy    }
125b1ef292b3d0c2b4b4c77bb7b442df8e73d1fbb5eIsaac Levy
126bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy    /**
127bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy     * @return time to response. Negative value on error.
128bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy     */
129bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy    public long pingDns(InetAddress dnsAddress, int timeout) {
130bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        DatagramSocket socket = null;
131bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        try {
132bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            socket = new DatagramSocket();
133bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
134bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            // Set some socket properties
135bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            socket.setSoTimeout(timeout);
136bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
1373ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levy            // Try to bind but continue ping if bind fails
1383ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levy            try {
1393ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levy                socket.setNetworkInterface(NetworkInterface.getByName(
1403ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levy                        getCurrentLinkProperties().getInterfaceName()));
1413ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levy            } catch (Exception e) {
1423ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levy                Slog.d(TAG,"pingDns::Error binding to socket", e);
1433ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levy            }
1443ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levy
1453ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levy            byte[] buf = constructQuery();
146bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
147bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            // Send the DNS query
148bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
149bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            DatagramPacket packet = new DatagramPacket(buf,
150bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy                    buf.length, dnsAddress, DNS_PORT);
151bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            long start = SystemClock.elapsedRealtime();
152bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            socket.send(packet);
153bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
154bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            // Wait for reply (blocks for the above timeout)
155bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            DatagramPacket replyPacket = new DatagramPacket(buf, buf.length);
156bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            socket.receive(replyPacket);
157bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
158bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            // If a timeout occurred, an exception would have been thrown. We
159bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            // got a reply!
160bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            return SystemClock.elapsedRealtime() - start;
161bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
162bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        } catch (SocketTimeoutException e) {
163bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            // Squelch this exception.
164bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            return -1;
165bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        } catch (Exception e) {
166bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            if (V) {
167bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy                Slog.v(TAG, "DnsPinger.pingDns got socket exception: ", e);
168bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            }
169bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            return -2;
170bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        } finally {
171bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            if (socket != null) {
172bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy                socket.close();
173bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            }
174bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        }
175bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
176bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy    }
177bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
1783ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levy    /**
1793ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levy     * @return google.com DNS query packet
1803ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levy     */
1813ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levy    private static byte[] constructQuery() {
1823ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levy        byte[] buf = new byte[DNS_QUERY_BASE_SIZE];
183bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
184bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        // [0-1] bytes are an ID, generate random ID for this query
185bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        buf[0] = (byte) sRandom.nextInt(256);
186bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        buf[1] = (byte) sRandom.nextInt(256);
187bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
188bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        // [2-3] bytes are for flags.
1893ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levy        buf[2] = 0x01; // Recursion desired
190bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
1913ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levy        // [4-5] bytes are for number of queries (QCOUNT)
1923ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levy        buf[5] = 0x01;
193bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
194bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        // [6-7] [8-9] [10-11] are all counts of other fields we don't use
195bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
196bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        // [12-15] for www
197bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        writeString(buf, 12, "www");
198bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
1993ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levy        // [16-22] for google
2003ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levy        writeString(buf, 16, "google");
2013ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levy
2023ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levy        // [23-26] for com
2033ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levy        writeString(buf, 23, "com");
2043ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levy
2053ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levy        // [27] is a null byte terminator byte for the url
206bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
2073ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levy        // [28-29] bytes are for QTYPE, set to 1 = A (host address)
2083ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levy        buf[29] = 0x01;
209bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
2103ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levy        // [30-31] bytes are for QCLASS, set to 1 = IN (internet)
2113ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levy        buf[31] = 0x01;
212bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
2133ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levy        return buf;
214bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy    }
215bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
2163ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levy    /**
2173ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levy     * Writes the string's length and its contents to the buffer
2183ee9d05d971cbf0c720057aec54ffac289af40feIsaac Levy     */
219bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy    private static void writeString(byte[] buf, int startPos, String string) {
220bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        int pos = startPos;
221bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy
222bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        // Write the length first
223bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        buf[pos++] = (byte) string.length();
224bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        for (int i = 0; i < string.length(); i++) {
225bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy            buf[pos++] = (byte) string.charAt(i);
226bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy        }
227bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy    }
228bc7dfb58bffea133ccf6d94470a26f8d193f4890Isaac Levy}
229