Home | History | Annotate | Download | only in net
      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 
     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
     61     public ConnectivityMetricsLogger(IConnectivityMetricsLogger service) {
     62         mService = service;
     63     }
     64 
     65     /** {@hide} */
     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      */
     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      */
    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      */
    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 
    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