diff options
10 files changed, 386 insertions, 5 deletions
diff --git a/Android.mk b/Android.mk index d813c9190e62..309c2db44258 100644 --- a/Android.mk +++ b/Android.mk @@ -202,6 +202,7 @@ LOCAL_SRC_FILES += \ core/java/android/net/IIpConnectivityMetrics.aidl \ core/java/android/net/IEthernetManager.aidl \ core/java/android/net/IEthernetServiceListener.aidl \ + core/java/android/net/INetdEventCallback.aidl \ core/java/android/net/INetworkManagementEventObserver.aidl \ core/java/android/net/INetworkPolicyListener.aidl \ core/java/android/net/INetworkPolicyManager.aidl \ diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index a2f9bdd2370d..0bb7150ee361 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -6609,4 +6609,46 @@ public class DevicePolicyManager { throw re.rethrowFromSystemServer(); } } + + /** + * Called by a device owner to control the network logging feature. Logging can only be + * enabled on single user devices where the sole user is managed by the device owner. If a new + * user is added on the device, logging is disabled. + * + * <p> Network logs contain DNS lookup and connect() library call events. + * + * @param admin Which {@link DeviceAdminReceiver} this request is associated with. + * @param enabled whether network logging should be enabled or not. + * @throws {@link SecurityException} if {@code admin} is not a device owner. + * @throws {@link RemoteException} if network logging could not be enabled or disabled due to + * the logging service not being available + * + * @hide + */ + public void setNetworkLoggingEnabled(@NonNull ComponentName admin, boolean enabled) { + throwIfParentInstance("setNetworkLoggingEnabled"); + try { + mService.setNetworkLoggingEnabled(admin, enabled); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** + * Return whether network logging is enabled by a device owner. + * + * @param admin Which {@link DeviceAdminReceiver} this request is associated with. + * @return {@code true} if network logging is enabled by device owner, {@code false} otherwise. + * @throws {@link SecurityException} if {@code admin} is not a device owner. + * + * @hide + */ + public boolean isNetworkLoggingEnabled(@NonNull ComponentName admin) { + throwIfParentInstance("isNetworkLoggingEnabled"); + try { + return mService.isNetworkLoggingEnabled(admin); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index f39cb5ae9fbd..a15e1e53379e 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -311,4 +311,7 @@ interface IDevicePolicyManager { void setBackupServiceEnabled(in ComponentName admin, boolean enabled); boolean isBackupServiceEnabled(in ComponentName admin); + + void setNetworkLoggingEnabled(in ComponentName admin, boolean enabled); + boolean isNetworkLoggingEnabled(in ComponentName admin); } diff --git a/core/java/android/net/IIpConnectivityMetrics.aidl b/core/java/android/net/IIpConnectivityMetrics.aidl index d36b7661aaa3..6f07b3153833 100644 --- a/core/java/android/net/IIpConnectivityMetrics.aidl +++ b/core/java/android/net/IIpConnectivityMetrics.aidl @@ -18,6 +18,7 @@ package android.net; import android.os.Parcelable; import android.net.ConnectivityMetricsEvent; +import android.net.INetdEventCallback; /** {@hide} */ interface IIpConnectivityMetrics { @@ -27,4 +28,13 @@ interface IIpConnectivityMetrics { * or -1 if the event was dropped due to rate limiting. */ int logEvent(in ConnectivityMetricsEvent event); + + /** + * At most one callback can be registered (by DevicePolicyManager). + * @return status {@code true} if registering/unregistering of the callback was successful, + * {@code false} otherwise (might happen if IIpConnectivityMetrics is not available, + * if it happens make sure you call it when the service is up in the caller) + */ + boolean registerNetdEventCallback(in INetdEventCallback callback); + boolean unregisterNetdEventCallback(); } diff --git a/core/java/android/net/INetdEventCallback.aidl b/core/java/android/net/INetdEventCallback.aidl new file mode 100644 index 000000000000..49436beadc51 --- /dev/null +++ b/core/java/android/net/INetdEventCallback.aidl @@ -0,0 +1,47 @@ +/* + * 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; + +/** {@hide} */ +oneway interface INetdEventCallback { + + /** + * Reports a single DNS lookup function call. + * This method must not block or perform long-running operations. + * + * @param hostname the name that was looked up. + * @param ipAddresses (possibly a subset of) the IP addresses returned. + * At most {@link #DNS_REPORTED_IP_ADDRESSES_LIMIT} addresses are logged. + * @param ipAddressesCount the number of IP addresses returned. May be different from the length + * of ipAddresses if there were too many addresses to log. + * @param timestamp the timestamp at which the query was reported by netd. + * @param uid the UID of the application that performed the query. + */ + void onDnsEvent(String hostname, in String[] ipAddresses, int ipAddressesCount, long timestamp, + int uid); + + /** + * Reports a single connect library call. + * This method must not block or perform long-running operations. + * + * @param ipAddr destination IP address. + * @param port destination port number. + * @param timestamp the timestamp at which the call was reported by netd. + * @param uid the UID of the application that performed the connection. + */ + void onConnectEvent(String ipAddr, int port, long timestamp, int uid); +} diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java index 6654814a0157..f485f56306a4 100644 --- a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java +++ b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java @@ -19,10 +19,13 @@ package com.android.server.connectivity; import android.content.Context; import android.net.ConnectivityMetricsEvent; import android.net.IIpConnectivityMetrics; +import android.net.INetdEventCallback; import android.net.metrics.ApfProgramEvent; import android.net.metrics.IpConnectivityLog; +import android.os.Binder; import android.os.IBinder; import android.os.Parcelable; +import android.os.Process; import android.provider.Settings; import android.text.TextUtils; import android.text.format.DateUtils; @@ -260,6 +263,33 @@ final public class IpConnectivityMetrics extends SystemService { private void enforcePermission(String what) { getContext().enforceCallingOrSelfPermission(what, "IpConnectivityMetrics"); } + + private void enforceNetdEventListeningPermission() { + final int uid = Binder.getCallingUid(); + if (uid != Process.SYSTEM_UID) { + throw new SecurityException(String.format("Uid %d has no permission to listen for" + + " netd events.", uid)); + } + } + + @Override + public boolean registerNetdEventCallback(INetdEventCallback callback) { + enforceNetdEventListeningPermission(); + if (mNetdListener == null) { + return false; + } + return mNetdListener.registerNetdEventCallback(callback); + } + + @Override + public boolean unregisterNetdEventCallback() { + enforceNetdEventListeningPermission(); + if (mNetdListener == null) { + // if the service is null, we aren't registered anyway + return true; + } + return mNetdListener.unregisterNetdEventCallback(); + } }; private static final ToIntFunction<Context> READ_BUFFER_SIZE = (ctx) -> { diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java index 4b175d7a5af8..8450482f4032 100644 --- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java +++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java @@ -20,10 +20,12 @@ import android.content.Context; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; import android.net.Network; +import android.net.INetdEventCallback; import android.net.NetworkRequest; import android.net.metrics.DnsEvent; import android.net.metrics.INetdEventListener; import android.net.metrics.IpConnectivityLog; +import android.os.RemoteException; import android.util.Log; import com.android.internal.annotations.GuardedBy; @@ -119,6 +121,21 @@ public class NetdEventListenerService extends INetdEventListener.Stub { } }; + // Callback should only be registered/unregistered when logging is being enabled/disabled in DPM + // by the device owner. It's DevicePolicyManager's responsibility to ensure that. + @GuardedBy("this") + private INetdEventCallback mNetdEventCallback; + + public synchronized boolean registerNetdEventCallback(INetdEventCallback callback) { + mNetdEventCallback = callback; + return true; + } + + public synchronized boolean unregisterNetdEventCallback() { + mNetdEventCallback = null; + return true; + } + public NetdEventListenerService(Context context) { this(context.getSystemService(ConnectivityManager.class), new IpConnectivityLog()); } @@ -136,7 +153,8 @@ public class NetdEventListenerService extends INetdEventListener.Stub { // Called concurrently by multiple binder threads. // This method must not block or perform long-running operations. public synchronized void onDnsEvent(int netId, int eventType, int returnCode, int latencyMs, - String hostname, String[] ipAddresses, int ipAddressesCount, int uid) { + String hostname, String[] ipAddresses, int ipAddressesCount, int uid) + throws RemoteException { maybeVerboseLog(String.format("onDnsEvent(%d, %d, %d, %d)", netId, eventType, returnCode, latencyMs)); @@ -146,14 +164,23 @@ public class NetdEventListenerService extends INetdEventListener.Stub { mEventBatches.put(netId, batch); } batch.addResult((byte) eventType, (byte) returnCode, latencyMs); + + if (mNetdEventCallback != null) { + mNetdEventCallback.onDnsEvent(hostname, ipAddresses, ipAddressesCount, + System.currentTimeMillis(), uid); + } } @Override // Called concurrently by multiple binder threads. // This method must not block or perform long-running operations. public synchronized void onConnectEvent(int netId, int latencyMs, String ipAddr, int port, - int uid) { + int uid) throws RemoteException { maybeVerboseLog(String.format("onConnectEvent(%d, %d)", netId, latencyMs)); + + if (mNetdEventCallback != null) { + mNetdEventCallback.onConnectEvent(ipAddr, port, System.currentTimeMillis(), uid); + } } public synchronized void dump(PrintWriter writer) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 96331e83dd31..7746e1eaa9f5 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -77,8 +77,10 @@ import android.graphics.Color; import android.media.AudioManager; import android.media.IAudioService; import android.net.ConnectivityManager; +import android.net.IIpConnectivityMetrics; import android.net.ProxyInfo; import android.net.Uri; +import android.net.metrics.IpConnectivityLog; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.os.AsyncTask; @@ -352,6 +354,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { boolean mIsWatch; private final SecurityLogMonitor mSecurityLogMonitor; + private NetworkLogger mNetworkLogger; private final AtomicBoolean mRemoteBugreportServiceIsActive = new AtomicBoolean(); private final AtomicBoolean mRemoteBugreportSharingAccepted = new AtomicBoolean(); @@ -478,6 +481,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, getSendingUserId()); + /* + * Network logging would ideally be started in setDeviceOwnerSystemPropertyLocked(), + * however it's too early in the boot process to register with IIpConnectivityMetrics + * to listen for events. + */ + if (Intent.ACTION_USER_STARTED.equals(action) + && userHandle == mOwners.getDeviceOwnerUserId() + && isNetworkLoggingEnabledInternal()) { + setNetworkLoggingActiveInternal(true); + } if (Intent.ACTION_BOOT_COMPLETED.equals(action) && userHandle == mOwners.getDeviceOwnerUserId() && getDeviceOwnerRemoteBugreportUri() != null) { @@ -549,6 +562,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { private static final String TAG_DISABLE_ACCOUNT_MANAGEMENT = "disable-account-management"; private static final String TAG_REQUIRE_AUTO_TIME = "require_auto_time"; private static final String TAG_FORCE_EPHEMERAL_USERS = "force_ephemeral_users"; + private static final String TAG_IS_NETWORK_LOGGING_ENABLED = "is_network_logging_enabled"; private static final String TAG_ACCOUNT_TYPE = "account-type"; private static final String TAG_PERMITTED_ACCESSIBILITY_SERVICES = "permitted-accessiblity-services"; @@ -643,6 +657,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { boolean disableScreenCapture = false; // Can only be set by a device/profile owner. boolean requireAutoTime = false; // Can only be set by a device owner. boolean forceEphemeralUsers = false; // Can only be set by a device owner. + boolean isNetworkLoggingEnabled = false; // Can only be set by a device owner. ActiveAdmin parentAdmin; final boolean isParent; @@ -851,6 +866,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { out.attribute(null, ATTR_VALUE, Boolean.toString(forceEphemeralUsers)); out.endTag(null, TAG_FORCE_EPHEMERAL_USERS); } + if (isNetworkLoggingEnabled) { + out.startTag(null, TAG_IS_NETWORK_LOGGING_ENABLED); + out.attribute(null, ATTR_VALUE, Boolean.toString(isNetworkLoggingEnabled)); + out.endTag(null, TAG_IS_NETWORK_LOGGING_ENABLED); + } if (disabledKeyguardFeatures != DEF_KEYGUARD_FEATURES_DISABLED) { out.startTag(null, TAG_DISABLE_KEYGUARD_FEATURES); out.attribute(null, ATTR_VALUE, Integer.toString(disabledKeyguardFeatures)); @@ -1037,6 +1057,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } else if (TAG_FORCE_EPHEMERAL_USERS.equals(tag)) { forceEphemeralUsers = Boolean.parseBoolean( parser.getAttributeValue(null, ATTR_VALUE)); + } else if (TAG_IS_NETWORK_LOGGING_ENABLED.equals(tag)) { + isNetworkLoggingEnabled = Boolean.parseBoolean( + parser.getAttributeValue(null, ATTR_VALUE)); } else if (TAG_DISABLE_KEYGUARD_FEATURES.equals(tag)) { disabledKeyguardFeatures = Integer.parseInt( parser.getAttributeValue(null, ATTR_VALUE)); @@ -1277,6 +1300,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { pw.println(requireAutoTime); pw.print(prefix); pw.print("forceEphemeralUsers="); pw.println(forceEphemeralUsers); + pw.print(prefix); pw.print("isNetworkLoggingEnabled="); + pw.println(isNetworkLoggingEnabled); pw.print(prefix); pw.print("disabledKeyguardFeatures="); pw.println(disabledKeyguardFeatures); pw.print(prefix); pw.print("crossProfileWidgetProviders="); @@ -1403,6 +1428,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return mContext.getSystemService(NotificationManager.class); } + IIpConnectivityMetrics getIIpConnectivityMetrics() { + return (IIpConnectivityMetrics) IIpConnectivityMetrics.Stub.asInterface( + ServiceManager.getService(IpConnectivityLog.SERVICE_NAME)); + } + PowerManagerInternal getPowerManagerInternal() { return LocalServices.getService(PowerManagerInternal.class); } @@ -9040,6 +9070,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (!isDeviceOwnerManagedSingleUserDevice()) { mInjector.securityLogSetLoggingEnabledProperty(false); Slog.w(LOG_TAG, "Security logging turned off as it's no longer a single user device."); + + getDeviceOwnerAdminLocked().isNetworkLoggingEnabled = false; + saveSettingsLocked(mInjector.userHandleGetCallingUserId()); + setNetworkLoggingActiveInternal(false); + Slog.w(LOG_TAG, "Network logging turned off as it's no longer a single user" + + " device."); + if (mOwners.hasDeviceOwner()) { setBackupServiceEnabledInternal(false); Slog.w(LOG_TAG, "Backup is off as it's a managed device that has more that one user."); @@ -9386,4 +9423,62 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } } + + @Override + public synchronized void setNetworkLoggingEnabled(ComponentName admin, boolean enabled) { + if (!mHasFeature) { + return; + } + Preconditions.checkNotNull(admin); + ensureDeviceOwnerManagingSingleUser(admin); + + if (enabled == isNetworkLoggingEnabledInternal()) { + // already in the requested state + return; + } + getDeviceOwnerAdminLocked().isNetworkLoggingEnabled = enabled; + saveSettingsLocked(mInjector.userHandleGetCallingUserId()); + + setNetworkLoggingActiveInternal(enabled); + } + + private synchronized void setNetworkLoggingActiveInternal(boolean active) { + final long callingIdentity = mInjector.binderClearCallingIdentity(); + try { + if (active) { + mNetworkLogger = new NetworkLogger(this, mInjector.getPackageManagerInternal()); + if (!mNetworkLogger.startNetworkLogging()) { + mNetworkLogger = null; + Slog.wtf(LOG_TAG, "Network logging could not be started due to the logging" + + " service not being available yet."); + } + } else { + if (mNetworkLogger != null && !mNetworkLogger.stopNetworkLogging()) { + mNetworkLogger = null; + Slog.wtf(LOG_TAG, "Network logging could not be stopped due to the logging" + + " service not being available yet."); + } + mNetworkLogger = null; + } + } finally { + mInjector.binderRestoreCallingIdentity(callingIdentity); + } + } + + @Override + public boolean isNetworkLoggingEnabled(ComponentName admin) { + if (!mHasFeature) { + return false; + } + Preconditions.checkNotNull(admin); + synchronized (this) { + getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); + return isNetworkLoggingEnabledInternal(); + } + } + + private synchronized boolean isNetworkLoggingEnabledInternal() { + ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked(); + return (deviceOwner != null) && deviceOwner.isNetworkLoggingEnabled; + } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java new file mode 100644 index 000000000000..db17ca23aa5f --- /dev/null +++ b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java @@ -0,0 +1,121 @@ +/* + * 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.devicepolicy; + +import android.content.pm.PackageManagerInternal; +import android.net.IIpConnectivityMetrics; +import android.net.INetdEventCallback; +import android.os.RemoteException; +import android.util.Log; +import android.util.Slog; + +import com.android.server.ServiceThread; + +import java.util.ArrayList; +import java.util.List; + +/** + * A class for managing network logging. + * This class is not thread-safe, callers should synchronize access. + */ +final class NetworkLogger { + + private static final String TAG = NetworkLogger.class.getSimpleName(); + + private final DevicePolicyManagerService mDpm; + private final PackageManagerInternal mPm; + + private IIpConnectivityMetrics mIpConnectivityMetrics; + private boolean mIsLoggingEnabled; + + private final INetdEventCallback mNetdEventCallback = new INetdEventCallback.Stub() { + @Override + public void onDnsEvent(String hostname, String[] ipAddresses, int ipAddressesCount, + long timestamp, int uid) { + if (!mIsLoggingEnabled) { + return; + } + // TODO(mkarpinski): send msg with data to Handler + } + + @Override + public void onConnectEvent(String ipAddr, int port, long timestamp, int uid) { + if (!mIsLoggingEnabled) { + return; + } + // TODO(mkarpinski): send msg with data to Handler + } + }; + + NetworkLogger(DevicePolicyManagerService dpm, PackageManagerInternal pm) { + mDpm = dpm; + mPm = pm; + } + + private boolean checkIpConnectivityMetricsService() { + if (mIpConnectivityMetrics != null) { + return true; + } + final IIpConnectivityMetrics service = mDpm.mInjector.getIIpConnectivityMetrics(); + if (service == null) { + return false; + } + mIpConnectivityMetrics = service; + return true; + } + + boolean startNetworkLogging() { + Log.d(TAG, "Starting network logging."); + if (!checkIpConnectivityMetricsService()) { + // the IIpConnectivityMetrics service should have been present at this point + Slog.wtf(TAG, "Failed to register callback with IIpConnectivityMetrics."); + return false; + } + try { + if (mIpConnectivityMetrics.registerNetdEventCallback(mNetdEventCallback)) { + // TODO(mkarpinski): start a new ServiceThread, instantiate a Handler etc. + mIsLoggingEnabled = true; + return true; + } else { + return false; + } + } catch (RemoteException re) { + Slog.wtf(TAG, "Failed to make remote calls to register the callback", re); + return false; + } + } + + boolean stopNetworkLogging() { + Log.d(TAG, "Stopping network logging"); + // stop the logging regardless of whether we failed to unregister listener + mIsLoggingEnabled = false; + try { + if (!checkIpConnectivityMetricsService()) { + // the IIpConnectivityMetrics service should have been present at this point + Slog.wtf(TAG, "Failed to unregister callback with IIpConnectivityMetrics."); + // logging is forcefully disabled even if unregistering fails + return true; + } + return mIpConnectivityMetrics.unregisterNetdEventCallback(); + } catch (RemoteException re) { + Slog.wtf(TAG, "Failed to make remote calls to unregister the callback", re); + } finally { + // TODO(mkarpinski): quitSafely() the Handler + return true; + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/connectivity/NetdEventListenerServiceTest.java b/services/tests/servicestests/src/com/android/server/connectivity/NetdEventListenerServiceTest.java index 9e2fd6231ba9..af4a374bffdd 100644 --- a/services/tests/servicestests/src/com/android/server/connectivity/NetdEventListenerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/connectivity/NetdEventListenerServiceTest.java @@ -22,6 +22,7 @@ import android.net.Network; import android.net.metrics.DnsEvent; import android.net.metrics.INetdEventListener; import android.net.metrics.IpConnectivityLog; +import android.os.RemoteException; import junit.framework.TestCase; import org.junit.Before; @@ -157,9 +158,13 @@ public class NetdEventListenerServiceTest extends TestCase { } void log(int netId, int[] latencies) { - for (int l : latencies) { - mNetdEventListenerService.onDnsEvent(netId, EVENT_TYPE, RETURN_CODE, l, null, null, 0, - 0); + try { + for (int l : latencies) { + mNetdEventListenerService.onDnsEvent(netId, EVENT_TYPE, RETURN_CODE, l, null, null, + 0, 0); + } + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); } } |