Merge "ConnectivityMetricsLogger: Switch to "pull" model" into mm-wireless-dev
This commit is contained in:
committed by
Android Partner Code Review
commit
20ebbb3349
@ -190,7 +190,6 @@ LOCAL_SRC_FILES += \
|
|||||||
core/java/android/net/ICaptivePortal.aidl \
|
core/java/android/net/ICaptivePortal.aidl \
|
||||||
core/java/android/net/IConnectivityManager.aidl \
|
core/java/android/net/IConnectivityManager.aidl \
|
||||||
core/java/android/net/IConnectivityMetricsLogger.aidl \
|
core/java/android/net/IConnectivityMetricsLogger.aidl \
|
||||||
core/java/android/net/IConnectivityMetricsLoggerSubscriber.aidl \
|
|
||||||
core/java/android/net/IEthernetManager.aidl \
|
core/java/android/net/IEthernetManager.aidl \
|
||||||
core/java/android/net/IEthernetServiceListener.aidl \
|
core/java/android/net/IEthernetServiceListener.aidl \
|
||||||
core/java/android/net/INetworkManagementEventObserver.aidl \
|
core/java/android/net/INetworkManagementEventObserver.aidl \
|
||||||
|
@ -17,3 +17,4 @@
|
|||||||
package android.net;
|
package android.net;
|
||||||
|
|
||||||
parcelable ConnectivityMetricsEvent;
|
parcelable ConnectivityMetricsEvent;
|
||||||
|
parcelable ConnectivityMetricsEvent.Reference;
|
||||||
|
@ -79,4 +79,42 @@ public class ConnectivityMetricsEvent implements Parcelable {
|
|||||||
return String.format("ConnectivityMetricsEvent(%d, %d, %d)", timestamp,
|
return String.format("ConnectivityMetricsEvent(%d, %d, %d)", timestamp,
|
||||||
componentTag, eventTag);
|
componentTag, eventTag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** {@hide} */
|
||||||
|
public static class Reference implements Parcelable {
|
||||||
|
|
||||||
|
public long value;
|
||||||
|
|
||||||
|
public Reference(long ref) {
|
||||||
|
this.value = ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Implement the Parcelable interface */
|
||||||
|
public static final Parcelable.Creator<Reference> CREATOR
|
||||||
|
= new Parcelable.Creator<Reference> (){
|
||||||
|
public Reference createFromParcel(Parcel source) {
|
||||||
|
return new Reference(source.readLong());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Reference[] newArray(int size) {
|
||||||
|
return new Reference[size];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Implement the Parcelable interface */
|
||||||
|
@Override
|
||||||
|
public int describeContents() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Implement the Parcelable interface */
|
||||||
|
@Override
|
||||||
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
|
dest.writeLong(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void readFromParcel(Parcel in) {
|
||||||
|
value = in.readLong();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package android.net;
|
package android.net;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.ServiceManager;
|
import android.os.ServiceManager;
|
||||||
@ -28,14 +29,24 @@ public class ConnectivityMetricsLogger {
|
|||||||
public static final String CONNECTIVITY_METRICS_LOGGER_SERVICE = "connectivity_metrics_logger";
|
public static final String CONNECTIVITY_METRICS_LOGGER_SERVICE = "connectivity_metrics_logger";
|
||||||
|
|
||||||
// Component Tags
|
// Component Tags
|
||||||
public static final int COMPONENT_TAG_CONNECTIVITY = 1;
|
public static final int COMPONENT_TAG_CONNECTIVITY = 0;
|
||||||
public static final int COMPONENT_TAG_BLUETOOTH = 2;
|
public static final int COMPONENT_TAG_BLUETOOTH = 1;
|
||||||
public static final int COMPONENT_TAG_WIFI = 3;
|
public static final int COMPONENT_TAG_WIFI = 2;
|
||||||
public static final int COMPONENT_TAG_TELECOM = 4;
|
public static final int COMPONENT_TAG_TELECOM = 3;
|
||||||
public static final int COMPONENT_TAG_TELEPHONY = 5;
|
public static final int COMPONENT_TAG_TELEPHONY = 4;
|
||||||
|
|
||||||
|
public static final int NUMBER_OF_COMPONENTS = 5;
|
||||||
|
|
||||||
|
// Event Tag
|
||||||
|
public static final int TAG_SKIPPED_EVENTS = -1;
|
||||||
|
|
||||||
|
public static final String DATA_KEY_EVENTS_COUNT = "count";
|
||||||
|
|
||||||
private IConnectivityMetricsLogger mService;
|
private IConnectivityMetricsLogger mService;
|
||||||
|
|
||||||
|
private long mServiceUnblockedTimestampMillis = 0;
|
||||||
|
private int mNumSkippedEvents = 0;
|
||||||
|
|
||||||
public ConnectivityMetricsLogger() {
|
public ConnectivityMetricsLogger() {
|
||||||
mService = IConnectivityMetricsLogger.Stub.asInterface(ServiceManager.getService(
|
mService = IConnectivityMetricsLogger.Stub.asInterface(ServiceManager.getService(
|
||||||
CONNECTIVITY_METRICS_LOGGER_SERVICE));
|
CONNECTIVITY_METRICS_LOGGER_SERVICE));
|
||||||
@ -46,12 +57,51 @@ public class ConnectivityMetricsLogger {
|
|||||||
if (DBG) {
|
if (DBG) {
|
||||||
Log.d(TAG, "logEvent(" + componentTag + "," + eventTag + ") Service not ready");
|
Log.d(TAG, "logEvent(" + componentTag + "," + eventTag + ") Service not ready");
|
||||||
}
|
}
|
||||||
} else {
|
return;
|
||||||
try {
|
}
|
||||||
mService.logEvent(new ConnectivityMetricsEvent(timestamp, componentTag, eventTag, data));
|
|
||||||
} catch (RemoteException e) {
|
if (mServiceUnblockedTimestampMillis > 0) {
|
||||||
Log.e(TAG, "Error logging event " + e.getMessage());
|
if (System.currentTimeMillis() < mServiceUnblockedTimestampMillis) {
|
||||||
|
// Service is throttling events.
|
||||||
|
// Don't send new events because they will be dropped.
|
||||||
|
mNumSkippedEvents++;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ConnectivityMetricsEvent skippedEventsEvent = null;
|
||||||
|
if (mNumSkippedEvents > 0) {
|
||||||
|
// Log number of skipped events
|
||||||
|
Bundle b = new Bundle();
|
||||||
|
b.putInt(DATA_KEY_EVENTS_COUNT, mNumSkippedEvents);
|
||||||
|
skippedEventsEvent = new ConnectivityMetricsEvent(mServiceUnblockedTimestampMillis,
|
||||||
|
componentTag, TAG_SKIPPED_EVENTS, b);
|
||||||
|
|
||||||
|
mServiceUnblockedTimestampMillis = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectivityMetricsEvent event = new ConnectivityMetricsEvent(timestamp, componentTag,
|
||||||
|
eventTag, data);
|
||||||
|
|
||||||
|
try {
|
||||||
|
long result;
|
||||||
|
if (skippedEventsEvent == null) {
|
||||||
|
result = mService.logEvent(event);
|
||||||
|
} else {
|
||||||
|
result = mService.logEvents(new ConnectivityMetricsEvent[]
|
||||||
|
{skippedEventsEvent, event});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result == 0) {
|
||||||
|
mNumSkippedEvents = 0;
|
||||||
|
} else {
|
||||||
|
mNumSkippedEvents++;
|
||||||
|
if (result > 0) { // events are throttled
|
||||||
|
mServiceUnblockedTimestampMillis = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log.e(TAG, "Error logging event " + e.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,15 +16,28 @@
|
|||||||
|
|
||||||
package android.net;
|
package android.net;
|
||||||
|
|
||||||
|
import android.app.PendingIntent;
|
||||||
import android.net.ConnectivityMetricsEvent;
|
import android.net.ConnectivityMetricsEvent;
|
||||||
import android.net.IConnectivityMetricsLoggerSubscriber;
|
|
||||||
|
|
||||||
/** {@hide} */
|
/** {@hide} */
|
||||||
interface IConnectivityMetricsLogger {
|
interface IConnectivityMetricsLogger {
|
||||||
|
|
||||||
void logEvent(in ConnectivityMetricsEvent event);
|
/**
|
||||||
void logEvents(in ConnectivityMetricsEvent[] events);
|
* @return 0 on success
|
||||||
|
* <0 if error happened
|
||||||
|
* >0 timestamp after which new events will be accepted
|
||||||
|
*/
|
||||||
|
long logEvent(in ConnectivityMetricsEvent event);
|
||||||
|
long logEvents(in ConnectivityMetricsEvent[] events);
|
||||||
|
|
||||||
boolean subscribe(in IConnectivityMetricsLoggerSubscriber subscriber);
|
/**
|
||||||
void unsubscribe(in IConnectivityMetricsLoggerSubscriber subscriber);
|
* @param reference of the last event previously returned. The function will return
|
||||||
|
* events following it.
|
||||||
|
* If 0 then all events will be returned.
|
||||||
|
* After the function call it will contain reference of the last event.
|
||||||
|
*/
|
||||||
|
ConnectivityMetricsEvent[] getEvents(inout ConnectivityMetricsEvent.Reference reference);
|
||||||
|
|
||||||
|
boolean register(in PendingIntent newEventsIntent);
|
||||||
|
void unregister(in PendingIntent newEventsIntent);
|
||||||
}
|
}
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package android.net;
|
|
||||||
|
|
||||||
import android.net.ConnectivityMetricsEvent;
|
|
||||||
|
|
||||||
/** {@hide} */
|
|
||||||
oneway interface IConnectivityMetricsLoggerSubscriber {
|
|
||||||
|
|
||||||
void onEvents(in ConnectivityMetricsEvent[] events);
|
|
||||||
}
|
|
@ -18,18 +18,21 @@ package com.android.server.connectivity;
|
|||||||
|
|
||||||
import com.android.server.SystemService;
|
import com.android.server.SystemService;
|
||||||
|
|
||||||
|
import android.app.PendingIntent;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
import android.net.ConnectivityMetricsEvent;
|
import android.net.ConnectivityMetricsEvent;
|
||||||
import android.net.ConnectivityMetricsLogger;
|
import android.net.ConnectivityMetricsLogger;
|
||||||
import android.net.IConnectivityMetricsLogger;
|
import android.net.IConnectivityMetricsLogger;
|
||||||
import android.net.IConnectivityMetricsLoggerSubscriber;
|
import android.os.Binder;
|
||||||
import android.os.IBinder;
|
import android.os.Parcel;
|
||||||
import android.os.RemoteException;
|
import android.text.format.DateUtils;
|
||||||
import android.util.ArrayMap;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.io.FileDescriptor;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.util.ArrayDeque;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/** {@hide} */
|
/** {@hide} */
|
||||||
public class MetricsLoggerService extends SystemService {
|
public class MetricsLoggerService extends SystemService {
|
||||||
@ -43,134 +46,307 @@ public class MetricsLoggerService extends SystemService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStart() {
|
public void onStart() {
|
||||||
|
resetThrottlingCounters(System.currentTimeMillis());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBootPhase(int phase) {
|
public void onBootPhase(int phase) {
|
||||||
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
|
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
|
||||||
Log.d(TAG, "onBootPhase: PHASE_SYSTEM_SERVICES_READY");
|
if (DBG) Log.d(TAG, "onBootPhase: PHASE_SYSTEM_SERVICES_READY");
|
||||||
publishBinderService(ConnectivityMetricsLogger.CONNECTIVITY_METRICS_LOGGER_SERVICE,
|
publishBinderService(ConnectivityMetricsLogger.CONNECTIVITY_METRICS_LOGGER_SERVICE,
|
||||||
mBinder);
|
mBinder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final int MAX_NUMBER_OF_EVENTS = 100;
|
// TODO: read from system property
|
||||||
private final int MAX_TIME_OFFSET = 15*60*1000; // 15 minutes
|
private final int MAX_NUMBER_OF_EVENTS = 1000;
|
||||||
private final List<ConnectivityMetricsEvent> mEvents = new ArrayList<>();
|
|
||||||
private long mLastSentEventTimeMillis = System.currentTimeMillis();
|
|
||||||
|
|
||||||
private final void enforceConnectivityInternalPermission() {
|
// TODO: read from system property
|
||||||
|
private final int EVENTS_NOTIFICATION_THRESHOLD = 300;
|
||||||
|
|
||||||
|
// TODO: read from system property
|
||||||
|
private final int THROTTLING_TIME_INTERVAL_MILLIS = 60 * 60 * 1000; // 1 hour
|
||||||
|
|
||||||
|
// TODO: read from system property
|
||||||
|
private final int THROTTLING_MAX_NUMBER_OF_MESSAGES_PER_COMPONENT = 1000;
|
||||||
|
|
||||||
|
private int mEventCounter = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference of the last event in the list of cached events.
|
||||||
|
*
|
||||||
|
* When client of this service retrieves events by calling getEvents, it is passing
|
||||||
|
* ConnectivityMetricsEvent.Reference object. After getEvents returns, that object will
|
||||||
|
* contain this reference. The client can save it and use next time it calls getEvents.
|
||||||
|
* This way only new events will be returned.
|
||||||
|
*/
|
||||||
|
private long mLastEventReference = 0;
|
||||||
|
|
||||||
|
private final int mThrottlingCounters[] =
|
||||||
|
new int[ConnectivityMetricsLogger.NUMBER_OF_COMPONENTS];
|
||||||
|
|
||||||
|
private long mThrottlingIntervalBoundaryMillis;
|
||||||
|
|
||||||
|
private final ArrayDeque<ConnectivityMetricsEvent> mEvents = new ArrayDeque<>();
|
||||||
|
|
||||||
|
private void enforceConnectivityInternalPermission() {
|
||||||
getContext().enforceCallingPermission(
|
getContext().enforceCallingPermission(
|
||||||
android.Manifest.permission.CONNECTIVITY_INTERNAL,
|
android.Manifest.permission.CONNECTIVITY_INTERNAL,
|
||||||
"MetricsLoggerService");
|
"MetricsLoggerService");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void enforceDumpPermission() {
|
||||||
|
getContext().enforceCallingPermission(
|
||||||
|
android.Manifest.permission.DUMP,
|
||||||
|
"MetricsLoggerService");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resetThrottlingCounters(long currentTimeMillis) {
|
||||||
|
for (int i = 0; i < mThrottlingCounters.length; i++) {
|
||||||
|
mThrottlingCounters[i] = 0;
|
||||||
|
}
|
||||||
|
mThrottlingIntervalBoundaryMillis =
|
||||||
|
currentTimeMillis + THROTTLING_TIME_INTERVAL_MILLIS;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addEvent(ConnectivityMetricsEvent e) {
|
||||||
|
if (VDBG) {
|
||||||
|
Log.v(TAG, "writeEvent(" + e.toString() + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
while (mEvents.size() >= MAX_NUMBER_OF_EVENTS) {
|
||||||
|
mEvents.removeFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
mEvents.addLast(e);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of the IConnectivityMetricsLogger interface.
|
* Implementation of the IConnectivityMetricsLogger interface.
|
||||||
*/
|
*/
|
||||||
private final IConnectivityMetricsLogger.Stub mBinder = new IConnectivityMetricsLogger.Stub() {
|
private final IConnectivityMetricsLogger.Stub mBinder = new IConnectivityMetricsLogger.Stub() {
|
||||||
|
|
||||||
private final ArrayMap<IConnectivityMetricsLoggerSubscriber,
|
private final ArrayList<PendingIntent> mPendingIntents = new ArrayList<>();
|
||||||
IBinder.DeathRecipient> mSubscribers = new ArrayMap<>();
|
|
||||||
|
|
||||||
|
@Override
|
||||||
private ConnectivityMetricsEvent[] prepareEventsToSendIfReady() {
|
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
||||||
ConnectivityMetricsEvent[] eventsToSend = null;
|
if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
|
||||||
final long currentTimeMillis = System.currentTimeMillis();
|
!= PackageManager.PERMISSION_GRANTED) {
|
||||||
final long timeOffset = currentTimeMillis - mLastSentEventTimeMillis;
|
pw.println("Permission Denial: can't dump ConnectivityMetricsLoggerService " +
|
||||||
if (timeOffset >= MAX_TIME_OFFSET
|
"from from pid=" + Binder.getCallingPid() + ", uid=" +
|
||||||
|| timeOffset < 0 // system time has changed
|
Binder.getCallingUid());
|
||||||
|| mEvents.size() >= MAX_NUMBER_OF_EVENTS) {
|
return;
|
||||||
// batch events
|
|
||||||
mLastSentEventTimeMillis = currentTimeMillis;
|
|
||||||
eventsToSend = new ConnectivityMetricsEvent[mEvents.size()];
|
|
||||||
mEvents.toArray(eventsToSend);
|
|
||||||
mEvents.clear();
|
|
||||||
}
|
}
|
||||||
return eventsToSend;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void maybeSendEventsToSubscribers(ConnectivityMetricsEvent[] eventsToSend) {
|
boolean dumpSerializedSize = false;
|
||||||
if (eventsToSend == null || eventsToSend.length == 0) return;
|
boolean dumpEvents = false;
|
||||||
synchronized (mSubscribers) {
|
for (String arg : args) {
|
||||||
for (IConnectivityMetricsLoggerSubscriber s : mSubscribers.keySet()) {
|
switch (arg) {
|
||||||
try {
|
case "--events":
|
||||||
s.onEvents(eventsToSend);
|
dumpEvents = true;
|
||||||
} catch (RemoteException ex) {
|
break;
|
||||||
Log.e(TAG, "RemoteException " + ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void logEvent(ConnectivityMetricsEvent event) {
|
case "--size":
|
||||||
ConnectivityMetricsEvent[] events = new ConnectivityMetricsEvent[]{event};
|
dumpSerializedSize = true;
|
||||||
logEvents(events);
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
public void logEvents(ConnectivityMetricsEvent[] events) {
|
case "--all":
|
||||||
enforceConnectivityInternalPermission();
|
dumpEvents = true;
|
||||||
ConnectivityMetricsEvent[] eventsToSend;
|
dumpSerializedSize = true;
|
||||||
|
break;
|
||||||
if (VDBG) {
|
|
||||||
for (ConnectivityMetricsEvent e : events) {
|
|
||||||
Log.v(TAG, "writeEvent(" + e.toString() + ")");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized (mEvents) {
|
synchronized (mEvents) {
|
||||||
for (ConnectivityMetricsEvent e : events) {
|
pw.println("Number of events: " + mEvents.size());
|
||||||
mEvents.add(e);
|
pw.println("Time span: " +
|
||||||
|
DateUtils.formatElapsedTime(
|
||||||
|
(System.currentTimeMillis() - mEvents.peekFirst().timestamp)
|
||||||
|
/ 1000));
|
||||||
|
|
||||||
|
if (dumpSerializedSize) {
|
||||||
|
long dataSize = 0;
|
||||||
|
Parcel p = Parcel.obtain();
|
||||||
|
for (ConnectivityMetricsEvent e : mEvents) {
|
||||||
|
dataSize += 16; // timestamp and 2 stamps
|
||||||
|
|
||||||
|
p.writeParcelable(e.data, 0);
|
||||||
|
}
|
||||||
|
dataSize += p.dataSize();
|
||||||
|
p.recycle();
|
||||||
|
pw.println("Serialized data size: " + dataSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
eventsToSend = prepareEventsToSendIfReady();
|
if (dumpEvents) {
|
||||||
|
pw.println();
|
||||||
|
pw.println("Events:");
|
||||||
|
for (ConnectivityMetricsEvent e : mEvents) {
|
||||||
|
pw.println(e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
maybeSendEventsToSubscribers(eventsToSend);
|
if (!mPendingIntents.isEmpty()) {
|
||||||
|
pw.println();
|
||||||
|
pw.println("Pending intents:");
|
||||||
|
for (PendingIntent pi : mPendingIntents) {
|
||||||
|
pw.println(pi.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean subscribe(IConnectivityMetricsLoggerSubscriber subscriber) {
|
public long logEvent(ConnectivityMetricsEvent event) {
|
||||||
enforceConnectivityInternalPermission();
|
ConnectivityMetricsEvent[] events = new ConnectivityMetricsEvent[]{event};
|
||||||
if (VDBG) Log.v(TAG, "subscribe");
|
return logEvents(events);
|
||||||
|
}
|
||||||
|
|
||||||
synchronized (mSubscribers) {
|
/**
|
||||||
if (mSubscribers.containsKey(subscriber)) {
|
* @param events
|
||||||
Log.e(TAG, "subscriber is already subscribed");
|
*
|
||||||
return false;
|
* Note: All events must belong to the same component.
|
||||||
|
*
|
||||||
|
* @return 0 on success
|
||||||
|
* <0 if error happened
|
||||||
|
* >0 timestamp after which new events will be accepted
|
||||||
|
*/
|
||||||
|
public long logEvents(ConnectivityMetricsEvent[] events) {
|
||||||
|
enforceConnectivityInternalPermission();
|
||||||
|
|
||||||
|
if (events == null || events.length == 0) {
|
||||||
|
Log.wtf(TAG, "No events passed to logEvents()");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int componentTag = events[0].componentTag;
|
||||||
|
if (componentTag < 0 ||
|
||||||
|
componentTag >= ConnectivityMetricsLogger.NUMBER_OF_COMPONENTS) {
|
||||||
|
Log.wtf(TAG, "Unexpected tag: " + componentTag);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized (mThrottlingCounters) {
|
||||||
|
long currentTimeMillis = System.currentTimeMillis();
|
||||||
|
if (currentTimeMillis > mThrottlingIntervalBoundaryMillis) {
|
||||||
|
resetThrottlingCounters(currentTimeMillis);
|
||||||
}
|
}
|
||||||
final IConnectivityMetricsLoggerSubscriber s = subscriber;
|
|
||||||
IBinder.DeathRecipient dr = new IBinder.DeathRecipient() {
|
mThrottlingCounters[componentTag] += events.length;
|
||||||
@Override
|
|
||||||
public void binderDied() {
|
if (mThrottlingCounters[componentTag] >
|
||||||
if (VDBG) Log.v(TAG, "subscriber died");
|
THROTTLING_MAX_NUMBER_OF_MESSAGES_PER_COMPONENT) {
|
||||||
synchronized (mSubscribers) {
|
Log.w(TAG, "Too many events from #" + componentTag +
|
||||||
mSubscribers.remove(s);
|
". Block until " + mThrottlingIntervalBoundaryMillis);
|
||||||
|
|
||||||
|
return mThrottlingIntervalBoundaryMillis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean sendPendingIntents = false;
|
||||||
|
|
||||||
|
synchronized (mEvents) {
|
||||||
|
for (ConnectivityMetricsEvent e : events) {
|
||||||
|
if (e.componentTag != componentTag) {
|
||||||
|
Log.wtf(TAG, "Unexpected tag: " + e.componentTag);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
addEvent(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
mLastEventReference += events.length;
|
||||||
|
|
||||||
|
mEventCounter += events.length;
|
||||||
|
if (mEventCounter >= EVENTS_NOTIFICATION_THRESHOLD) {
|
||||||
|
mEventCounter = 0;
|
||||||
|
sendPendingIntents = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sendPendingIntents) {
|
||||||
|
synchronized (mPendingIntents) {
|
||||||
|
for (PendingIntent pi : mPendingIntents) {
|
||||||
|
if (VDBG) Log.v(TAG, "Send pending intent");
|
||||||
|
try {
|
||||||
|
pi.send(getContext(), 0, null, null, null);
|
||||||
|
} catch (PendingIntent.CanceledException e) {
|
||||||
|
Log.e(TAG, "Pending intent canceled: " + pi);
|
||||||
|
mPendingIntents.remove(pi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
subscriber.asBinder().linkToDeath(dr, 0);
|
|
||||||
mSubscribers.put(subscriber, dr);
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
Log.e(TAG, "subscribe failed: " + e);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve events
|
||||||
|
*
|
||||||
|
* @param reference of the last event previously returned. The function will return
|
||||||
|
* events following it.
|
||||||
|
* If 0 then all events will be returned.
|
||||||
|
* After the function call it will contain reference of the
|
||||||
|
* last returned event.
|
||||||
|
* @return events
|
||||||
|
*/
|
||||||
|
public ConnectivityMetricsEvent[] getEvents(ConnectivityMetricsEvent.Reference reference) {
|
||||||
|
enforceDumpPermission();
|
||||||
|
long ref = reference.value;
|
||||||
|
if (VDBG) Log.v(TAG, "getEvents(" + ref + ")");
|
||||||
|
|
||||||
|
ConnectivityMetricsEvent[] result;
|
||||||
|
synchronized (mEvents) {
|
||||||
|
if (ref > mLastEventReference) {
|
||||||
|
Log.e(TAG, "Invalid reference");
|
||||||
|
reference.value = mLastEventReference;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (ref < mLastEventReference - mEvents.size()) {
|
||||||
|
ref = mLastEventReference - mEvents.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
int numEventsToSkip =
|
||||||
|
mEvents.size() // Total number of events
|
||||||
|
- (int)(mLastEventReference - ref); // Number of events to return
|
||||||
|
|
||||||
|
result = new ConnectivityMetricsEvent[mEvents.size() - numEventsToSkip];
|
||||||
|
int i = 0;
|
||||||
|
for (ConnectivityMetricsEvent e : mEvents) {
|
||||||
|
if (numEventsToSkip > 0) {
|
||||||
|
numEventsToSkip--;
|
||||||
|
} else {
|
||||||
|
result[i++] = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reference.value = mLastEventReference;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean register(PendingIntent newEventsIntent) {
|
||||||
|
enforceDumpPermission();
|
||||||
|
if (VDBG) Log.v(TAG, "register(" + newEventsIntent + ")");
|
||||||
|
|
||||||
|
synchronized (mPendingIntents) {
|
||||||
|
if (mPendingIntents.remove(newEventsIntent)) {
|
||||||
|
Log.w(TAG, "Replacing registered pending intent");
|
||||||
|
}
|
||||||
|
mPendingIntents.add(newEventsIntent);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unsubscribe(IConnectivityMetricsLoggerSubscriber subscriber) {
|
public void unregister(PendingIntent newEventsIntent) {
|
||||||
enforceConnectivityInternalPermission();
|
enforceDumpPermission();
|
||||||
if (VDBG) Log.v(TAG, "unsubscribe");
|
if (VDBG) Log.v(TAG, "unregister(" + newEventsIntent + ")");
|
||||||
synchronized (mSubscribers) {
|
|
||||||
IBinder.DeathRecipient dr = mSubscribers.remove(subscriber);
|
synchronized (mPendingIntents) {
|
||||||
if (dr == null) {
|
if (!mPendingIntents.remove(newEventsIntent)) {
|
||||||
Log.e(TAG, "subscriber is not subscribed");
|
Log.e(TAG, "Pending intent is not registered");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
subscriber.asBinder().unlinkToDeath(dr, 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user