2 * Copyright (C) 2016 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.server.connectivity;
19 import android.content.Context;
20 import android.net.metrics.DnsEvent;
21 import android.net.ConnectivityManager;
22 import android.net.ConnectivityManager.NetworkCallback;
23 import android.net.Network;
24 import android.net.NetworkRequest;
25 import android.net.metrics.IDnsEventListener;
26 import android.util.Log;
28 import com.android.internal.annotations.GuardedBy;
29 import com.android.internal.util.IndentingPrintWriter;
31 import java.io.PrintWriter;
32 import java.util.Arrays;
33 import java.util.SortedMap;
34 import java.util.TreeMap;
38 * Implementation of the IDnsEventListener interface.
40 public class DnsEventListenerService extends IDnsEventListener.Stub {
42 public static final String SERVICE_NAME = "dns_listener";
44 private static final String TAG = DnsEventListenerService.class.getSimpleName();
45 private static final boolean DBG = true;
46 private static final boolean VDBG = false;
48 private static final int MAX_LOOKUPS_PER_DNS_EVENT = 100;
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;
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;
61 public DnsEventBatch(int netId) {
65 public void addResult(byte eventType, byte returnCode, int latencyMs) {
66 mEventTypes[mEventCount] = eventType;
67 mReturnCodes[mEventCount] = returnCode;
68 mLatenciesMs[mEventCount] = latencyMs;
70 if (mEventCount == MAX_LOOKUPS_PER_DNS_EVENT) {
75 public void logAndClear() {
76 // Did we lose a race with addResult?
77 if (mEventCount == 0) {
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));
90 // For debugging and unit tests only.
91 public String toString() {
92 return String.format("%s %d %d", getClass().getSimpleName(), mNetId, mEventCount);
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.
99 private SortedMap<Integer, DnsEventBatch> mEventBatches = new TreeMap<>();
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() {
108 public void onLost(Network network) {
109 synchronized (DnsEventListenerService.this) {
110 DnsEventBatch batch = mEventBatches.remove(network.netId);
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()
123 mCm = context.getSystemService(ConnectivityManager.class);
124 mCm.registerNetworkCallback(request, mNetworkCallback);
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));
133 DnsEventBatch batch = mEventBatches.get(netId);
135 batch = new DnsEventBatch(netId);
136 mEventBatches.put(netId, batch);
138 batch.addResult((byte) eventType, (byte) returnCode, latencyMs);
141 public synchronized void dump(PrintWriter writer) {
142 IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
143 pw.println(TAG + ":");
145 for (DnsEventBatch batch : mEventBatches.values()) {
146 pw.println(batch.toString());
151 private static void maybeLog(String s) {
152 if (DBG) Log.d(TAG, s);
155 private static void maybeVerboseLog(String s) {
156 if (VDBG) Log.d(TAG, s);