blob: f6dc9b98c13677854f68bb988655adbb8c0bd212 [file] [log] [blame]
/*
* 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 com.android.server.connectivity;
import com.android.server.SystemService;
import android.content.Context;
import android.net.ConnectivityMetricsEvent;
import android.net.ConnectivityMetricsLogger;
import android.net.IConnectivityMetricsLogger;
import android.net.IConnectivityMetricsLoggerSubscriber;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
/** {@hide} */
public class MetricsLoggerService extends SystemService {
private static String TAG = "ConnectivityMetricsLoggerService";
private static final boolean DBG = true;
private static final boolean VDBG = false;
public MetricsLoggerService(Context context) {
super(context);
}
@Override
public void onStart() {
}
@Override
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
Log.d(TAG, "onBootPhase: PHASE_SYSTEM_SERVICES_READY");
publishBinderService(ConnectivityMetricsLogger.CONNECTIVITY_METRICS_LOGGER_SERVICE,
mBinder);
}
}
private final int MAX_NUMBER_OF_EVENTS = 100;
private final int MAX_TIME_OFFSET = 15*60*1000; // 15 minutes
private final List<ConnectivityMetricsEvent> mEvents = new ArrayList<>();
private long mLastSentEventTimeMillis = System.currentTimeMillis();
private final void enforceConnectivityInternalPermission() {
getContext().enforceCallingPermission(
android.Manifest.permission.CONNECTIVITY_INTERNAL,
"MetricsLoggerService");
}
/**
* Implementation of the IConnectivityMetricsLogger interface.
*/
private final IConnectivityMetricsLogger.Stub mBinder = new IConnectivityMetricsLogger.Stub() {
private final ArrayMap<IConnectivityMetricsLoggerSubscriber,
IBinder.DeathRecipient> mSubscribers = new ArrayMap<>();
private ConnectivityMetricsEvent[] prepareEventsToSendIfReady() {
ConnectivityMetricsEvent[] eventsToSend = null;
final long currentTimeMillis = System.currentTimeMillis();
final long timeOffset = currentTimeMillis - mLastSentEventTimeMillis;
if (timeOffset >= MAX_TIME_OFFSET
|| timeOffset < 0 // system time has changed
|| mEvents.size() >= MAX_NUMBER_OF_EVENTS) {
// batch events
mLastSentEventTimeMillis = currentTimeMillis;
eventsToSend = new ConnectivityMetricsEvent[mEvents.size()];
mEvents.toArray(eventsToSend);
mEvents.clear();
}
return eventsToSend;
}
private void maybeSendEventsToSubscribers(ConnectivityMetricsEvent[] eventsToSend) {
if (eventsToSend == null || eventsToSend.length == 0) return;
synchronized (mSubscribers) {
for (IConnectivityMetricsLoggerSubscriber s : mSubscribers.keySet()) {
try {
s.onEvents(eventsToSend);
} catch (RemoteException ex) {
Log.e(TAG, "RemoteException " + ex);
}
}
}
}
public void logEvent(ConnectivityMetricsEvent event) {
ConnectivityMetricsEvent[] events = new ConnectivityMetricsEvent[]{event};
logEvents(events);
}
public void logEvents(ConnectivityMetricsEvent[] events) {
enforceConnectivityInternalPermission();
ConnectivityMetricsEvent[] eventsToSend;
if (VDBG) {
for (ConnectivityMetricsEvent e : events) {
Log.v(TAG, "writeEvent(" + e.toString() + ")");
}
}
synchronized (mEvents) {
for (ConnectivityMetricsEvent e : events) {
mEvents.add(e);
}
eventsToSend = prepareEventsToSendIfReady();
}
maybeSendEventsToSubscribers(eventsToSend);
}
public boolean subscribe(IConnectivityMetricsLoggerSubscriber subscriber) {
enforceConnectivityInternalPermission();
if (VDBG) Log.v(TAG, "subscribe");
synchronized (mSubscribers) {
if (mSubscribers.containsKey(subscriber)) {
Log.e(TAG, "subscriber is already subscribed");
return false;
}
final IConnectivityMetricsLoggerSubscriber s = subscriber;
IBinder.DeathRecipient dr = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
if (VDBG) Log.v(TAG, "subscriber died");
synchronized (mSubscribers) {
mSubscribers.remove(s);
}
}
};
try {
subscriber.asBinder().linkToDeath(dr, 0);
mSubscribers.put(subscriber, dr);
} catch (RemoteException e) {
Log.e(TAG, "subscribe failed: " + e);
return false;
}
}
return true;
}
public void unsubscribe(IConnectivityMetricsLoggerSubscriber subscriber) {
enforceConnectivityInternalPermission();
if (VDBG) Log.v(TAG, "unsubscribe");
synchronized (mSubscribers) {
IBinder.DeathRecipient dr = mSubscribers.remove(subscriber);
if (dr == null) {
Log.e(TAG, "subscriber is not subscribed");
return;
}
subscriber.asBinder().unlinkToDeath(dr, 0);
}
}
};
}