ConnectivityMetricsLogger.java revision dc5a63bdd153f32d68962bc8271079295b41e1ee
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 */
16package android.net;
17
18import android.annotation.SystemApi;
19import android.app.PendingIntent;
20import android.os.Bundle;
21import android.os.Parcelable;
22import android.os.RemoteException;
23import android.os.ServiceManager;
24import android.util.Log;
25
26import com.android.internal.annotations.VisibleForTesting;
27
28/** {@hide} */
29@SystemApi
30public class ConnectivityMetricsLogger {
31    private static String TAG = "ConnectivityMetricsLogger";
32    private static final boolean DBG = true;
33
34    public static final String CONNECTIVITY_METRICS_LOGGER_SERVICE = "connectivity_metrics_logger";
35
36    // Component Tags
37    public static final int COMPONENT_TAG_CONNECTIVITY = 0;
38    public static final int COMPONENT_TAG_BLUETOOTH    = 1;
39    public static final int COMPONENT_TAG_WIFI         = 2;
40    public static final int COMPONENT_TAG_TELECOM      = 3;
41    public static final int COMPONENT_TAG_TELEPHONY    = 4;
42    public static final int NUMBER_OF_COMPONENTS       = 5;
43
44    // Event Tag
45    public static final int TAG_SKIPPED_EVENTS = -1;
46
47    public static final String DATA_KEY_EVENTS_COUNT = "count";
48
49    /** {@hide} */ protected final IConnectivityMetricsLogger mService;
50    /** {@hide} */ protected volatile long mServiceUnblockedTimestampMillis;
51    private int mNumSkippedEvents;
52
53    public ConnectivityMetricsLogger() {
54        this(IConnectivityMetricsLogger.Stub.asInterface(
55                ServiceManager.getService(CONNECTIVITY_METRICS_LOGGER_SERVICE)));
56    }
57
58    /** {@hide} */
59    @VisibleForTesting
60    public ConnectivityMetricsLogger(IConnectivityMetricsLogger service) {
61        mService = service;
62    }
63
64    /**
65     * Log a ConnectivityMetricsEvent.
66     *
67     * This method keeps track of skipped events when MetricsLoggerService throttles input events.
68     * It skips logging when MetricsLoggerService is active. When throttling ends, it logs a
69     * meta-event containing the number of events dropped. It is not safe to call this method
70     * concurrently from different threads.
71     *
72     * @param timestamp is the epoch timestamp of the event in ms.
73     * @param componentTag is the COMPONENT_* constant the event belongs to.
74     * @param eventTag is an event type constant whose meaning is specific to the component tag.
75     * @param data is a Parcelable instance representing the event.
76     */
77    public void logEvent(long timestamp, int componentTag, int eventTag, Parcelable data) {
78        if (mService == null) {
79            if (DBG) {
80                Log.d(TAG, "logEvent(" + componentTag + "," + eventTag + ") Service not ready");
81            }
82            return;
83        }
84
85        if (mServiceUnblockedTimestampMillis > 0) {
86            if (System.currentTimeMillis() < mServiceUnblockedTimestampMillis) {
87                // Service is throttling events.
88                // Don't send new events because they will be dropped.
89                mNumSkippedEvents++;
90                return;
91            }
92        }
93
94        ConnectivityMetricsEvent skippedEventsEvent = null;
95        if (mNumSkippedEvents > 0) {
96            // Log number of skipped events
97            Bundle b = new Bundle();
98            b.putInt(DATA_KEY_EVENTS_COUNT, mNumSkippedEvents);
99
100            // Log the skipped event.
101            // TODO: Note that some of the clients push all states events into the server,
102            // If we lose some states logged here, we might mess up the statistics happened at the
103            // backend. One of the options is to introduce a non-skippable flag for important events
104            // that are logged.
105            skippedEventsEvent = new ConnectivityMetricsEvent(mServiceUnblockedTimestampMillis,
106                    componentTag, TAG_SKIPPED_EVENTS, b);
107
108            mServiceUnblockedTimestampMillis = 0;
109        }
110
111        ConnectivityMetricsEvent event = new ConnectivityMetricsEvent(timestamp, componentTag,
112                eventTag, data);
113
114        try {
115            long result;
116            if (skippedEventsEvent == null) {
117                result = mService.logEvent(event);
118            } else {
119                result = mService.logEvents(new ConnectivityMetricsEvent[]
120                        {skippedEventsEvent, event});
121            }
122
123            if (result == 0) {
124                mNumSkippedEvents = 0;
125            } else {
126                mNumSkippedEvents++;
127                if (result > 0) { // events are throttled
128                    mServiceUnblockedTimestampMillis = result;
129                }
130            }
131        } catch (RemoteException e) {
132            Log.e(TAG, "Error logging event", e);
133        }
134    }
135
136    /**
137     * Retrieve events
138     *
139     * @param reference of the last event previously returned. The function will return
140     *                  events following it.
141     *                  If 0 then all events will be returned.
142     *                  After the function call it will contain reference of the
143     *                  last returned event.
144     * @return events
145     */
146    public ConnectivityMetricsEvent[] getEvents(ConnectivityMetricsEvent.Reference reference) {
147        try {
148            return mService.getEvents(reference);
149        } catch (RemoteException e) {
150            Log.e(TAG, "IConnectivityMetricsLogger.getEvents", e);
151            return null;
152        }
153    }
154
155    /**
156     * Register PendingIntent which will be sent when new events are ready to be retrieved.
157     */
158    public boolean register(PendingIntent newEventsIntent) {
159        try {
160            return mService.register(newEventsIntent);
161        } catch (RemoteException e) {
162            Log.e(TAG, "IConnectivityMetricsLogger.register", e);
163            return false;
164        }
165    }
166
167    public boolean unregister(PendingIntent newEventsIntent) {
168        try {
169            mService.unregister(newEventsIntent);
170            return true;
171        } catch (RemoteException e) {
172            Log.e(TAG, "IConnectivityMetricsLogger.unregister", e);
173            return false;
174        }
175    }
176}
177