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