[go: nahoru, domu]

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 com.android.server.connectivity;
18
19import android.content.Context;
20import android.net.metrics.DnsEvent;
21import android.net.ConnectivityManager;
22import android.net.ConnectivityManager.NetworkCallback;
23import android.net.Network;
24import android.net.NetworkRequest;
25import android.net.metrics.IDnsEventListener;
26import android.util.Log;
27
28import com.android.internal.annotations.GuardedBy;
29import com.android.internal.util.IndentingPrintWriter;
30
31import java.io.PrintWriter;
32import java.util.Arrays;
33import java.util.SortedMap;
34import java.util.TreeMap;
35
36
37/**
38 * Implementation of the IDnsEventListener interface.
39 */
40public class DnsEventListenerService extends IDnsEventListener.Stub {
41
42    public static final String SERVICE_NAME = "dns_listener";
43
44    private static final String TAG = DnsEventListenerService.class.getSimpleName();
45    private static final boolean DBG = true;
46    private static final boolean VDBG = false;
47
48    private static final int MAX_LOOKUPS_PER_DNS_EVENT = 100;
49
50    // Stores the results of a number of consecutive DNS lookups on the same network.
51    // This class is not thread-safe and it is the responsibility of the service to call its methods
52    // on one thread at a time.
53    private static class DnsEventBatch {
54        private final int mNetId;
55
56        private final byte[] mEventTypes = new byte[MAX_LOOKUPS_PER_DNS_EVENT];
57        private final byte[] mReturnCodes = new byte[MAX_LOOKUPS_PER_DNS_EVENT];
58        private final int[] mLatenciesMs = new int[MAX_LOOKUPS_PER_DNS_EVENT];
59        private int mEventCount;
60
61        public DnsEventBatch(int netId) {
62            mNetId = netId;
63        }
64
65        public void addResult(byte eventType, byte returnCode, int latencyMs) {
66            mEventTypes[mEventCount] = eventType;
67            mReturnCodes[mEventCount] = returnCode;
68            mLatenciesMs[mEventCount] = latencyMs;
69            mEventCount++;
70            if (mEventCount == MAX_LOOKUPS_PER_DNS_EVENT) {
71                logAndClear();
72            }
73        }
74
75        public void logAndClear() {
76            // Did we lose a race with addResult?
77            if (mEventCount == 0) {
78                return;
79            }
80
81            // Only log as many events as we actually have.
82            byte[] eventTypes = Arrays.copyOf(mEventTypes, mEventCount);
83            byte[] returnCodes = Arrays.copyOf(mReturnCodes, mEventCount);
84            int[] latenciesMs = Arrays.copyOf(mLatenciesMs, mEventCount);
85            DnsEvent.logEvent(mNetId, eventTypes, returnCodes, latenciesMs);
86            maybeLog(String.format("Logging %d results for netId %d", mEventCount, mNetId));
87            mEventCount = 0;
88        }
89
90        // For debugging and unit tests only.
91        public String toString() {
92            return String.format("%s %d %d", getClass().getSimpleName(), mNetId, mEventCount);
93        }
94    }
95
96    // Only sorted for ease of debugging. Because we only typically have a handful of networks up
97    // at any given time, performance is not a concern.
98    @GuardedBy("this")
99    private SortedMap<Integer, DnsEventBatch> mEventBatches = new TreeMap<>();
100
101    // We register a NetworkCallback to ensure that when a network disconnects, we flush the DNS
102    // queries we've logged on that network. Because we do not do this periodically, we might lose
103    // up to MAX_LOOKUPS_PER_DNS_EVENT lookup stats on each network when the system is shutting
104    // down. We believe this to be sufficient for now.
105    private final ConnectivityManager mCm;
106    private final NetworkCallback mNetworkCallback = new NetworkCallback() {
107        @Override
108        public void onLost(Network network) {
109            synchronized (DnsEventListenerService.this) {
110                DnsEventBatch batch = mEventBatches.remove(network.netId);
111                if (batch != null) {
112                    batch.logAndClear();
113                }
114            }
115        }
116    };
117
118    public DnsEventListenerService(Context context) {
119        // We are started when boot is complete, so ConnectivityService should already be running.
120        final NetworkRequest request = new NetworkRequest.Builder()
121            .clearCapabilities()
122            .build();
123        mCm = context.getSystemService(ConnectivityManager.class);
124        mCm.registerNetworkCallback(request, mNetworkCallback);
125    }
126
127    @Override
128    // Called concurrently by multiple binder threads.
129    public synchronized void onDnsEvent(int netId, int eventType, int returnCode, int latencyMs) {
130        maybeVerboseLog(String.format("onDnsEvent(%d, %d, %d, %d)",
131                netId, eventType, returnCode, latencyMs));
132
133        DnsEventBatch batch = mEventBatches.get(netId);
134        if (batch == null) {
135            batch = new DnsEventBatch(netId);
136            mEventBatches.put(netId, batch);
137        }
138        batch.addResult((byte) eventType, (byte) returnCode, latencyMs);
139    }
140
141    public synchronized void dump(PrintWriter writer) {
142        IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
143        pw.println(TAG + ":");
144        pw.increaseIndent();
145        for (DnsEventBatch batch : mEventBatches.values()) {
146            pw.println(batch.toString());
147        }
148        pw.decreaseIndent();
149    }
150
151    private static void maybeLog(String s) {
152        if (DBG) Log.d(TAG, s);
153    }
154
155    private static void maybeVerboseLog(String s) {
156        if (VDBG) Log.d(TAG, s);
157    }
158}
159