OSDN Git Service

DO NOT MERGE. Grant MMS Uri permissions as the calling UID.
[android-x86/frameworks-base.git] / services / core / java / com / android / server / connectivity / DnsEventListenerService.java
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
17 package com.android.server.connectivity;
18
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;
27
28 import com.android.internal.annotations.GuardedBy;
29 import com.android.internal.util.IndentingPrintWriter;
30
31 import java.io.PrintWriter;
32 import java.util.Arrays;
33 import java.util.SortedMap;
34 import java.util.TreeMap;
35
36
37 /**
38  * Implementation of the IDnsEventListener interface.
39  */
40 public 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 }