diff options
| -rw-r--r-- | Android.mk | 2 | ||||
| -rw-r--r-- | api/current.txt | 11 | ||||
| -rw-r--r-- | api/system-current.txt | 11 | ||||
| -rw-r--r-- | core/java/android/net/ConnectivityMetricsEvent.aidl | 19 | ||||
| -rw-r--r-- | core/java/android/net/ConnectivityMetricsEvent.java | 82 | ||||
| -rw-r--r-- | core/java/android/net/ConnectivityMetricsLogger.java | 57 | ||||
| -rw-r--r-- | core/java/android/net/IConnectivityMetricsLogger.aidl | 30 | ||||
| -rw-r--r-- | core/java/android/net/IConnectivityMetricsLoggerSubscriber.aidl | 25 | ||||
| -rw-r--r-- | services/core/java/com/android/server/connectivity/MetricsLoggerService.java | 177 | ||||
| -rw-r--r-- | services/java/com/android/server/SystemServer.java | 5 |
10 files changed, 419 insertions, 0 deletions
diff --git a/Android.mk b/Android.mk index 716477806bd1..017ee949f3b7 100644 --- a/Android.mk +++ b/Android.mk @@ -189,6 +189,8 @@ LOCAL_SRC_FILES += \ core/java/android/hardware/usb/IUsbManager.aidl \ core/java/android/net/ICaptivePortal.aidl \ core/java/android/net/IConnectivityManager.aidl \ + core/java/android/net/IConnectivityMetricsLogger.aidl \ + core/java/android/net/IConnectivityMetricsLoggerSubscriber.aidl \ core/java/android/net/IEthernetManager.aidl \ core/java/android/net/IEthernetServiceListener.aidl \ core/java/android/net/INetworkManagementEventObserver.aidl \ diff --git a/api/current.txt b/api/current.txt index f7608b00651e..b4cad03c48e3 100644 --- a/api/current.txt +++ b/api/current.txt @@ -18207,6 +18207,17 @@ package android.net { method public abstract void onNetworkActive(); } + public class ConnectivityMetricsEvent implements android.os.Parcelable { + ctor public ConnectivityMetricsEvent(long, int, int, android.os.Parcelable); + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.net.ConnectivityMetricsEvent> CREATOR; + field public final int componentTag; + field public final android.os.Parcelable data; + field public final int eventTag; + field public final long timestamp; + } + public class Credentials { ctor public Credentials(int, int, int); method public int getGid(); diff --git a/api/system-current.txt b/api/system-current.txt index a03f3283a532..3dcfcd13964c 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -19720,6 +19720,17 @@ package android.net { method public abstract void onNetworkActive(); } + public class ConnectivityMetricsEvent implements android.os.Parcelable { + ctor public ConnectivityMetricsEvent(long, int, int, android.os.Parcelable); + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.net.ConnectivityMetricsEvent> CREATOR; + field public final int componentTag; + field public final android.os.Parcelable data; + field public final int eventTag; + field public final long timestamp; + } + public class Credentials { ctor public Credentials(int, int, int); method public int getGid(); diff --git a/core/java/android/net/ConnectivityMetricsEvent.aidl b/core/java/android/net/ConnectivityMetricsEvent.aidl new file mode 100644 index 000000000000..da175614b588 --- /dev/null +++ b/core/java/android/net/ConnectivityMetricsEvent.aidl @@ -0,0 +1,19 @@ +/* + * 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; + +parcelable ConnectivityMetricsEvent; diff --git a/core/java/android/net/ConnectivityMetricsEvent.java b/core/java/android/net/ConnectivityMetricsEvent.java new file mode 100644 index 000000000000..d040a8563440 --- /dev/null +++ b/core/java/android/net/ConnectivityMetricsEvent.java @@ -0,0 +1,82 @@ +/* + * 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.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +@SystemApi +public class ConnectivityMetricsEvent implements Parcelable { + + /** The time when this event was collected, as returned by System.currentTimeMillis(). */ + final public long timestamp; + + /** The subsystem that generated the event. One of the COMPONENT_TAG_xxx constants. */ + final public int componentTag; + + /** The subsystem-specific event ID. */ + final public int eventTag; + + /** Opaque event-specific data. */ + final public Parcelable data; + + public ConnectivityMetricsEvent(long timestamp, int componentTag, + int eventTag, Parcelable data) { + this.timestamp = timestamp; + this.componentTag = componentTag; + this.eventTag = eventTag; + this.data = data; + } + + /** Implement the Parcelable interface */ + public static final Parcelable.Creator<ConnectivityMetricsEvent> CREATOR + = new Parcelable.Creator<ConnectivityMetricsEvent> (){ + public ConnectivityMetricsEvent createFromParcel(Parcel source) { + final long timestamp = source.readLong(); + final int componentTag = source.readInt(); + final int eventTag = source.readInt(); + final Parcelable data = source.readParcelable(null); + return new ConnectivityMetricsEvent(timestamp, componentTag, + eventTag, data); + } + + public ConnectivityMetricsEvent[] newArray(int size) { + return new ConnectivityMetricsEvent[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(timestamp); + dest.writeInt(componentTag); + dest.writeInt(eventTag); + dest.writeParcelable(data, 0); + } + + public String toString() { + return String.format("ConnectivityMetricsEvent(%d, %d, %d)", timestamp, + componentTag, eventTag); + } +} diff --git a/core/java/android/net/ConnectivityMetricsLogger.java b/core/java/android/net/ConnectivityMetricsLogger.java new file mode 100644 index 000000000000..3ef805017f14 --- /dev/null +++ b/core/java/android/net/ConnectivityMetricsLogger.java @@ -0,0 +1,57 @@ +/* + * 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.os.Parcelable; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Log; + +/** {@hide} */ +public class ConnectivityMetricsLogger { + private static String TAG = "ConnectivityMetricsLogger"; + private static final boolean DBG = true; + + public static final String CONNECTIVITY_METRICS_LOGGER_SERVICE = "connectivity_metrics_logger"; + + // Component Tags + public static final int COMPONENT_TAG_CONNECTIVITY = 1; + public static final int COMPONENT_TAG_BLUETOOTH = 2; + public static final int COMPONENT_TAG_WIFI = 3; + public static final int COMPONENT_TAG_TELECOM = 4; + public static final int COMPONENT_TAG_TELEPHONY = 5; + + private IConnectivityMetricsLogger mService; + + public ConnectivityMetricsLogger() { + mService = IConnectivityMetricsLogger.Stub.asInterface(ServiceManager.getService( + CONNECTIVITY_METRICS_LOGGER_SERVICE)); + } + + public void logEvent(long timestamp, int componentTag, int eventTag, Parcelable data) { + if (mService == null) { + if (DBG) { + Log.d(TAG, "logEvent(" + componentTag + "," + eventTag + ") Service not ready"); + } + } else { + try { + mService.logEvent(new ConnectivityMetricsEvent(timestamp, componentTag, eventTag, data)); + } catch (RemoteException e) { + Log.e(TAG, "Error logging event " + e.getMessage()); + } + } + } +} diff --git a/core/java/android/net/IConnectivityMetricsLogger.aidl b/core/java/android/net/IConnectivityMetricsLogger.aidl new file mode 100644 index 000000000000..27786712a5c5 --- /dev/null +++ b/core/java/android/net/IConnectivityMetricsLogger.aidl @@ -0,0 +1,30 @@ +/* + * 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; +import android.net.IConnectivityMetricsLoggerSubscriber; + +/** {@hide} */ +interface IConnectivityMetricsLogger { + + void logEvent(in ConnectivityMetricsEvent event); + void logEvents(in ConnectivityMetricsEvent[] events); + + boolean subscribe(in IConnectivityMetricsLoggerSubscriber subscriber); + void unsubscribe(in IConnectivityMetricsLoggerSubscriber subscriber); +} diff --git a/core/java/android/net/IConnectivityMetricsLoggerSubscriber.aidl b/core/java/android/net/IConnectivityMetricsLoggerSubscriber.aidl new file mode 100644 index 000000000000..a2c62cdaee97 --- /dev/null +++ b/core/java/android/net/IConnectivityMetricsLoggerSubscriber.aidl @@ -0,0 +1,25 @@ +/* + * 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); +} diff --git a/services/core/java/com/android/server/connectivity/MetricsLoggerService.java b/services/core/java/com/android/server/connectivity/MetricsLoggerService.java new file mode 100644 index 000000000000..f6dc9b98c136 --- /dev/null +++ b/services/core/java/com/android/server/connectivity/MetricsLoggerService.java @@ -0,0 +1,177 @@ +/* + * 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); + } + } + }; +} diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 81c5a1ac55d5..09a1100c2e21 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -56,6 +56,7 @@ import com.android.server.am.ActivityManagerService; import com.android.server.audio.AudioService; import com.android.server.camera.CameraService; import com.android.server.clipboard.ClipboardService; +import com.android.server.connectivity.MetricsLoggerService; import com.android.server.content.ContentService; import com.android.server.devicepolicy.DevicePolicyManagerService; import com.android.server.display.DisplayManagerService; @@ -552,6 +553,10 @@ public final class SystemServer { } else { mSystemServiceManager.startService(BluetoothService.class); } + + traceBeginAndSlog("ConnectivityMetricsLoggerService"); + mSystemServiceManager.startService(MetricsLoggerService.class); + Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); } catch (RuntimeException e) { Slog.e("System", "******************************************"); Slog.e("System", "************ Failure starting core service", e); |