diff options
11 files changed, 332 insertions, 71 deletions
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 7ae413892733..4b12bc48b49e 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -175,6 +175,9 @@ public class ConnectivityService extends IConnectivityManager.Stub implements PendingIntent.OnFinished { private static final String TAG = ConnectivityService.class.getSimpleName(); + public static final String DIAG_ARG = "--diag"; + public static final String SHORT_ARG = "--short"; + private static final boolean DBG = true; private static final boolean VDBG = false; @@ -1852,7 +1855,7 @@ public class ConnectivityService extends IConnectivityManager.Stub final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; - if (argsContain(args, "--diag")) { + if (argsContain(args, DIAG_ARG)) { dumpNetworkDiagnostics(pw); return; } @@ -1938,7 +1941,7 @@ public class ConnectivityService extends IConnectivityManager.Stub pw.decreaseIndent(); } - if (argsContain(args, "--short") == false) { + if (argsContain(args, SHORT_ARG) == false) { pw.println(); synchronized (mValidationLogs) { pw.println("mValidationLogs (most recent first):"); diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java index 81a14582dae1..901092ed3175 100644 --- a/services/core/java/com/android/server/connectivity/Tethering.java +++ b/services/core/java/com/android/server/connectivity/Tethering.java @@ -20,6 +20,7 @@ import static android.hardware.usb.UsbManager.USB_CONNECTED; import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE; import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED; +import static com.android.server.ConnectivityService.SHORT_ARG; import android.app.Notification; import android.app.NotificationManager; @@ -47,6 +48,7 @@ import android.net.NetworkRequest; import android.net.NetworkState; import android.net.NetworkUtils; import android.net.RouteInfo; +import android.net.util.SharedLog; import android.net.wifi.WifiManager; import android.os.Binder; import android.os.Bundle; @@ -62,7 +64,6 @@ import android.telephony.CarrierConfigManager; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.ArrayMap; -import android.util.LocalLog; import android.util.Log; import android.util.SparseArray; @@ -147,9 +148,7 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering } } - private final static int MAX_LOG_RECORDS = 500; - - private final LocalLog mLocalLog = new LocalLog(MAX_LOG_RECORDS); + private final SharedLog mLog = new SharedLog(TAG); // used to synchronize public access to members private final Object mPublicSync; @@ -180,7 +179,7 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering public Tethering(Context context, INetworkManagementService nmService, INetworkStatsService statsService, INetworkPolicyManager policyManager, Looper looper, MockableSystemProperties systemProperties) { - mLocalLog.log("CONSTRUCTED"); + mLog.mark("constructed"); mContext = context; mNMService = nmService; mStatsService = statsService; @@ -195,9 +194,9 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering mTetherMasterSM = new TetherMasterSM("TetherMaster", mLooper); mTetherMasterSM.start(); - mOffloadController = new OffloadController(mTetherMasterSM.getHandler()); + mOffloadController = new OffloadController(mTetherMasterSM.getHandler(), mLog); mUpstreamNetworkMonitor = new UpstreamNetworkMonitor( - mContext, mTetherMasterSM, TetherMasterSM.EVENT_UPSTREAM_CALLBACK); + mContext, mTetherMasterSM, TetherMasterSM.EVENT_UPSTREAM_CALLBACK, mLog); mForwardedDownstreams = new HashSet<>(); mStateReceiver = new StateReceiver(); @@ -1094,7 +1093,7 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering addState(mSetDnsForwardersErrorState); mNotifyList = new ArrayList<>(); - mIPv6TetheringCoordinator = new IPv6TetheringCoordinator(mNotifyList); + mIPv6TetheringCoordinator = new IPv6TetheringCoordinator(mNotifyList, mLog); setInitialState(mInitialState); } @@ -1141,7 +1140,7 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering try { mNMService.setIpForwardingEnabled(true); } catch (Exception e) { - mLocalLog.log("ERROR " + e); + mLog.e(e); transitionTo(mSetIpForwardingEnabledErrorState); return false; } @@ -1154,12 +1153,12 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering mNMService.stopTethering(); mNMService.startTethering(cfg.dhcpRanges); } catch (Exception ee) { - mLocalLog.log("ERROR " + ee); + mLog.e(ee); transitionTo(mStartTetheringErrorState); return false; } } - mLocalLog.log("SET master tether settings: ON"); + mLog.log("SET master tether settings: ON"); return true; } @@ -1167,19 +1166,19 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering try { mNMService.stopTethering(); } catch (Exception e) { - mLocalLog.log("ERROR " + e); + mLog.e(e); transitionTo(mStopTetheringErrorState); return false; } try { mNMService.setIpForwardingEnabled(false); } catch (Exception e) { - mLocalLog.log("ERROR " + e); + mLog.e(e); transitionTo(mSetIpForwardingDisabledErrorState); return false; } transitionTo(mInitialState); - mLocalLog.log("SET master tether settings: OFF"); + mLog.log("SET master tether settings: OFF"); return true; } @@ -1305,13 +1304,13 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering } try { mNMService.setDnsForwarders(network, dnsServers); - mLocalLog.log(String.format( - "SET DNS forwarders: network=%s dnsServers=[%s]", + mLog.log(String.format( + "SET DNS forwarders: network=%s dnsServers=%s", network, Arrays.toString(dnsServers))); } catch (Exception e) { // TODO: Investigate how this can fail and what exactly // happens if/when such failures occur. - mLocalLog.log("ERROR setting DNS forwarders failed, " + e); + mLog.e("setting DNS forwarders failed, " + e); transitionTo(mSetDnsForwardersErrorState); } } @@ -1788,12 +1787,23 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering pw.println("Log:"); pw.increaseIndent(); - mLocalLog.readOnlyLocalLog().dump(fd, pw, args); + if (argsContain(args, SHORT_ARG)) { + pw.println("<log removed for brevity>"); + } else { + mLog.dump(fd, pw, args); + } pw.decreaseIndent(); pw.decreaseIndent(); } + private static boolean argsContain(String[] args, String target) { + for (String arg : args) { + if (arg.equals(target)) return true; + } + return false; + } + @Override public void notifyInterfaceStateChange(String iface, TetherInterfaceStateMachine who, int state, int error) { @@ -1807,8 +1817,7 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering } } - mLocalLog.log(String.format("OBSERVED iface=%s state=%s error=%s", - iface, state, error)); + mLog.log(String.format("OBSERVED iface=%s state=%s error=%s", iface, state, error)); try { // Notify that we're tethering (or not) this interface. @@ -1846,8 +1855,8 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering private void trackNewTetherableInterface(String iface, int interfaceType) { TetherState tetherState; tetherState = new TetherState(new TetherInterfaceStateMachine(iface, mLooper, - interfaceType, mNMService, mStatsService, this, - new IPv6TetheringInterfaceServices(iface, mNMService))); + interfaceType, mLog, mNMService, mStatsService, this, + new IPv6TetheringInterfaceServices(iface, mNMService, mLog))); mTetherStates.put(iface, tetherState); tetherState.stateMachine.start(); } diff --git a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java index 248565405e5e..518f6c1440cc 100644 --- a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java +++ b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java @@ -25,6 +25,7 @@ import android.net.NetworkCapabilities; import android.net.NetworkState; import android.net.RouteInfo; import android.net.util.NetworkConstants; +import android.net.util.SharedLog; import android.util.Log; import java.net.Inet6Address; @@ -64,6 +65,7 @@ public class IPv6TetheringCoordinator { } private final ArrayList<TetherInterfaceStateMachine> mNotifyList; + private final SharedLog mLog; // NOTE: mActiveDownstreams is a list and not a hash data structure because // we keep active downstreams in arrival order. This is done so /64s can // be parceled out on a "first come, first served" basis and a /64 used by @@ -74,8 +76,10 @@ public class IPv6TetheringCoordinator { private short mNextSubnetId; private NetworkState mUpstreamNetworkState; - public IPv6TetheringCoordinator(ArrayList<TetherInterfaceStateMachine> notifyList) { + public IPv6TetheringCoordinator(ArrayList<TetherInterfaceStateMachine> notifyList, + SharedLog log) { mNotifyList = notifyList; + mLog = log.forSubComponent(TAG); mActiveDownstreams = new LinkedList<>(); mUniqueLocalPrefix = generateUniqueLocalPrefix(); mNextSubnetId = 0; @@ -115,7 +119,7 @@ public class IPv6TetheringCoordinator { if (VDBG) { Log.d(TAG, "updateUpstreamNetworkState: " + toDebugString(ns)); } - if (!canTetherIPv6(ns)) { + if (!canTetherIPv6(ns, mLog)) { stopIPv6TetheringOnAllInterfaces(); setUpstreamNetworkState(null); return; @@ -150,9 +154,7 @@ public class IPv6TetheringCoordinator { null); } - if (DBG) { - Log.d(TAG, "setUpstreamNetworkState: " + toDebugString(mUpstreamNetworkState)); - } + mLog.log("setUpstreamNetworkState: " + toDebugString(mUpstreamNetworkState)); } private void updateIPv6TetheringInterfaces() { @@ -206,7 +208,7 @@ public class IPv6TetheringCoordinator { return null; } - private static boolean canTetherIPv6(NetworkState ns) { + private static boolean canTetherIPv6(NetworkState ns, SharedLog sharedLog) { // Broadly speaking: // // [1] does the upstream have an IPv6 default route? @@ -260,13 +262,11 @@ public class IPv6TetheringCoordinator { final boolean outcome = canTether && supportedConfiguration; - if (VDBG) { - if (ns == null) { - Log.d(TAG, "No available upstream."); - } else { - Log.d(TAG, String.format("IPv6 tethering is %s for upstream: %s", - (outcome ? "available" : "not available"), toDebugString(ns))); - } + if (ns == null) { + sharedLog.log("No available upstream."); + } else { + sharedLog.log(String.format("IPv6 tethering is %s for upstream: %s", + (outcome ? "available" : "not available"), toDebugString(ns))); } return outcome; diff --git a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java index c6a7925f2b5e..adf4af846e8e 100644 --- a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java +++ b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java @@ -28,10 +28,10 @@ import android.net.RouteInfo; import android.net.ip.RouterAdvertisementDaemon; import android.net.ip.RouterAdvertisementDaemon.RaParams; import android.net.util.NetdService; +import android.net.util.SharedLog; import android.os.INetworkManagementService; import android.os.ServiceSpecificException; import android.os.RemoteException; -import android.util.Log; import android.util.Slog; import java.net.Inet6Address; @@ -54,6 +54,7 @@ public class IPv6TetheringInterfaceServices { private final String mIfName; private final INetworkManagementService mNMService; + private final SharedLog mLog; private NetworkInterface mNetworkInterface; private byte[] mHwAddr; @@ -61,9 +62,11 @@ public class IPv6TetheringInterfaceServices { private RouterAdvertisementDaemon mRaDaemon; private RaParams mLastRaParams; - public IPv6TetheringInterfaceServices(String ifname, INetworkManagementService nms) { + public IPv6TetheringInterfaceServices( + String ifname, INetworkManagementService nms, SharedLog log) { mIfName = ifname; mNMService = nms; + mLog = log.forSubComponent(mIfName); } public boolean start() { @@ -72,12 +75,12 @@ public class IPv6TetheringInterfaceServices { try { mNetworkInterface = NetworkInterface.getByName(mIfName); } catch (SocketException e) { - Log.e(TAG, "Error looking up NetworkInterfaces for " + mIfName, e); + mLog.e("Error looking up NetworkInterfaces: " + e); stop(); return false; } if (mNetworkInterface == null) { - Log.e(TAG, "Failed to find NetworkInterface for " + mIfName); + mLog.e("Failed to find NetworkInterface"); stop(); return false; } @@ -85,7 +88,7 @@ public class IPv6TetheringInterfaceServices { try { mHwAddr = mNetworkInterface.getHardwareAddress(); } catch (SocketException e) { - Log.e(TAG, "Failed to find hardware address for " + mIfName, e); + mLog.e("Failed to find hardware address: " + e); stop(); return false; } @@ -161,11 +164,11 @@ public class IPv6TetheringInterfaceServices { try { final int removalFailures = mNMService.removeRoutesFromLocalNetwork(toBeRemoved); if (removalFailures > 0) { - Log.e(TAG, String.format("Failed to remove %d IPv6 routes from local table.", + mLog.e(String.format("Failed to remove %d IPv6 routes from local table.", removalFailures)); } } catch (RemoteException e) { - Log.e(TAG, "Failed to remove IPv6 routes from local table: ", e); + mLog.e("Failed to remove IPv6 routes from local table: " + e); } } @@ -195,7 +198,7 @@ public class IPv6TetheringInterfaceServices { // error (EEXIST is silently ignored). mNMService.addInterfaceToLocalNetwork(mIfName, toBeAdded); } catch (RemoteException e) { - Log.e(TAG, "Failed to add IPv6 routes to local table: ", e); + mLog.e("Failed to add IPv6 routes to local table: " + e); } } } @@ -206,7 +209,7 @@ public class IPv6TetheringInterfaceServices { final INetd netd = NetdService.getInstance(); if (netd == null) { if (newDnses != null) newDnses.clear(); - Log.e(TAG, "No netd service instance available; not setting local IPv6 addresses"); + mLog.e("No netd service instance available; not setting local IPv6 addresses"); return; } @@ -217,7 +220,7 @@ public class IPv6TetheringInterfaceServices { try { netd.interfaceDelAddress(mIfName, dnsString, RFC7421_PREFIX_LENGTH); } catch (ServiceSpecificException | RemoteException e) { - Log.e(TAG, "Failed to remove local dns IP: " + dnsString, e); + mLog.e("Failed to remove local dns IP " + dnsString + ": " + e); } } } @@ -234,7 +237,7 @@ public class IPv6TetheringInterfaceServices { try { netd.interfaceAddAddress(mIfName, dnsString, RFC7421_PREFIX_LENGTH); } catch (ServiceSpecificException | RemoteException e) { - Log.e(TAG, "Failed to add local dns IP: " + dnsString, e); + mLog.e("Failed to add local dns IP " + dnsString + ": " + e); newDnses.remove(dns); } } @@ -243,7 +246,7 @@ public class IPv6TetheringInterfaceServices { try { netd.tetherApplyDnsInterfaces(); } catch (ServiceSpecificException | RemoteException e) { - Log.e(TAG, "Failed to update local DNS caching server"); + mLog.e("Failed to update local DNS caching server"); if (newDnses != null) newDnses.clear(); } } diff --git a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java index 220e7514facc..8f21d99b85e6 100644 --- a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java +++ b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java @@ -18,7 +18,7 @@ package com.android.server.connectivity.tethering; import android.net.LinkProperties; import android.os.Handler; -import android.util.Log; +import android.net.util.SharedLog; /** * A wrapper around hardware offload interface. @@ -29,16 +29,18 @@ public class OffloadController { private static final String TAG = OffloadController.class.getSimpleName(); private final Handler mHandler; + private final SharedLog mLog; private LinkProperties mUpstreamLinkProperties; - public OffloadController(Handler h) { + public OffloadController(Handler h, SharedLog log) { mHandler = h; + mLog = log.forSubComponent(TAG); } public void start() { // TODO: initOffload() and configure callbacks to be handled on our // preferred Handler. - Log.d(TAG, "tethering offload not supported"); + mLog.i("tethering offload not supported"); } public void stop() { diff --git a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java index d3cfd875faae..4a1d40590078 100644 --- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java +++ b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java @@ -22,6 +22,7 @@ import android.net.InterfaceConfiguration; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.NetworkUtils; +import android.net.util.SharedLog; import android.os.INetworkManagementService; import android.os.Looper; import android.os.Message; @@ -82,6 +83,7 @@ public class TetherInterfaceStateMachine extends StateMachine { private final State mTetheredState; private final State mUnavailableState; + private final SharedLog mLog; private final INetworkManagementService mNMService; private final INetworkStatsService mStatsService; private final IControlsTethering mTetherController; @@ -93,10 +95,12 @@ public class TetherInterfaceStateMachine extends StateMachine { private int mLastError; private String mMyUpstreamIfaceName; // may change over time - public TetherInterfaceStateMachine(String ifaceName, Looper looper, int interfaceType, - INetworkManagementService nMService, INetworkStatsService statsService, - IControlsTethering tetherController, IPv6TetheringInterfaceServices ipv6Svc) { + public TetherInterfaceStateMachine( + String ifaceName, Looper looper, int interfaceType, SharedLog log, + INetworkManagementService nMService, INetworkStatsService statsService, + IControlsTethering tetherController, IPv6TetheringInterfaceServices ipv6Svc) { super(ifaceName, looper); + mLog = log.forSubComponent(ifaceName); mNMService = nMService; mStatsService = statsService; mTetherController = tetherController; @@ -162,7 +166,7 @@ public class TetherInterfaceStateMachine extends StateMachine { mNMService.setInterfaceConfig(mIfaceName, ifcg); } } catch (Exception e) { - Log.e(TAG, "Error configuring interface " + mIfaceName, e); + mLog.e("Error configuring interface " + e); return false; } @@ -203,7 +207,7 @@ public class TetherInterfaceStateMachine extends StateMachine { transitionTo(mTetheredState); break; default: - Log.e(TAG, "Invalid tethering interface serving state specified."); + mLog.e("Invalid tethering interface serving state specified."); } break; case CMD_INTERFACE_DOWN: @@ -232,13 +236,13 @@ public class TetherInterfaceStateMachine extends StateMachine { try { mNMService.tetherInterface(mIfaceName); } catch (Exception e) { - Log.e(TAG, "Error Tethering: " + e.toString()); + mLog.e("Error Tethering: " + e); mLastError = ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR; return; } if (!mIPv6TetherSvc.start()) { - Log.e(TAG, "Failed to start IPv6TetheringInterfaceServices"); + mLog.e("Failed to start IPv6TetheringInterfaceServices"); // TODO: Make this a fatal error once Bluetooth IPv6 is sorted. return; } @@ -255,7 +259,7 @@ public class TetherInterfaceStateMachine extends StateMachine { mNMService.untetherInterface(mIfaceName); } catch (Exception e) { mLastError = ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR; - Log.e(TAG, "Failed to untether interface: " + e.toString()); + mLog.e("Failed to untether interface: " + e); } configureIfaceIp(false); @@ -316,7 +320,7 @@ public class TetherInterfaceStateMachine extends StateMachine { maybeLogMessage(this, message.what); switch (message.what) { case CMD_TETHER_REQUESTED: - Log.e(TAG, "CMD_TETHER_REQUESTED while in local hotspot mode."); + mLog.e("CMD_TETHER_REQUESTED while in local-only hotspot mode."); break; case CMD_TETHER_CONNECTION_CHANGED: // Ignored in local hotspot state. @@ -389,7 +393,7 @@ public class TetherInterfaceStateMachine extends StateMachine { boolean retValue = true; switch (message.what) { case CMD_TETHER_REQUESTED: - Log.e(TAG, "CMD_TETHER_REQUESTED while already tethering."); + mLog.e("CMD_TETHER_REQUESTED while already tethering."); break; case CMD_TETHER_CONNECTION_CHANGED: String newUpstreamIfaceName = (String)(message.obj); @@ -406,7 +410,7 @@ public class TetherInterfaceStateMachine extends StateMachine { mNMService.startInterfaceForwarding(mIfaceName, newUpstreamIfaceName); } catch (Exception e) { - Log.e(TAG, "Exception enabling Nat: " + e.toString()); + mLog.e("Exception enabling NAT: " + e); cleanupUpstreamInterface(newUpstreamIfaceName); mLastError = ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR; transitionTo(mInitialState); diff --git a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java index 97a2d5ed6f5c..be714907725b 100644 --- a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java +++ b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java @@ -29,6 +29,7 @@ import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkRequest; import android.net.NetworkState; +import android.net.util.SharedLog; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; @@ -73,6 +74,7 @@ public class UpstreamNetworkMonitor { private static final int CALLBACK_MOBILE_REQUEST = 3; private final Context mContext; + private final SharedLog mLog; private final StateMachine mTarget; private final Handler mHandler; private final int mWhat; @@ -84,16 +86,18 @@ public class UpstreamNetworkMonitor { private boolean mDunRequired; private Network mCurrentDefault; - public UpstreamNetworkMonitor(Context ctx, StateMachine tgt, int what) { + public UpstreamNetworkMonitor(Context ctx, StateMachine tgt, int what, SharedLog log) { mContext = ctx; mTarget = tgt; mHandler = mTarget.getHandler(); mWhat = what; + mLog = log.forSubComponent(TAG); } @VisibleForTesting - public UpstreamNetworkMonitor(StateMachine tgt, int what, ConnectivityManager cm) { - this(null, tgt, what); + public UpstreamNetworkMonitor( + StateMachine tgt, int what, ConnectivityManager cm, SharedLog log) { + this(null, tgt, what, log); mCM = cm; } @@ -136,7 +140,7 @@ public class UpstreamNetworkMonitor { public void registerMobileNetworkRequest() { if (mMobileNetworkCallback != null) { - Log.e(TAG, "registerMobileNetworkRequest() already registered"); + mLog.e("registerMobileNetworkRequest() already registered"); return; } @@ -156,7 +160,7 @@ public class UpstreamNetworkMonitor { // TODO: Change the timeout from 0 (no onUnavailable callback) to some // moderate callback timeout. This might be useful for updating some UI. // Additionally, we log a message to aid in any subsequent debugging. - Log.d(TAG, "requesting mobile upstream network: " + mobileUpstreamRequest); + mLog.i("requesting mobile upstream network: " + mobileUpstreamRequest); cm().requestNetwork(mobileUpstreamRequest, mMobileNetworkCallback, 0, legacyType, mHandler); } diff --git a/services/net/java/android/net/util/SharedLog.java b/services/net/java/android/net/util/SharedLog.java new file mode 100644 index 000000000000..343d237f8cd9 --- /dev/null +++ b/services/net/java/android/net/util/SharedLog.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2017 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.util; + +import android.text.TextUtils; +import android.util.LocalLog; +import android.util.Log; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.StringJoiner; + + +/** + * Class to centralize logging functionality for tethering. + * + * All access to class methods other than dump() must be on the same thread. + * + * @hide + */ +public class SharedLog { + private final static int DEFAULT_MAX_RECORDS = 500; + private final static String COMPONENT_DELIMITER = "."; + + private enum Category { + NONE, + ERROR, + MARK, + WARN, + }; + + private final LocalLog mLocalLog; + // The tag to use for output to the system log. This is not output to the + // LocalLog because that would be redundant. + private final String mTag; + // The component (or subcomponent) of a system that is sharing this log. + // This can grow in depth if components call forSubComponent() to obtain + // their SharedLog instance. The tag is not included in the component for + // brevity. + private final String mComponent; + + public SharedLog(String tag) { + this(DEFAULT_MAX_RECORDS, tag); + } + + public SharedLog(int maxRecords, String tag) { + this(new LocalLog(maxRecords), tag, tag); + } + + private SharedLog(LocalLog localLog, String tag, String component) { + mLocalLog = localLog; + mTag = tag; + mComponent = component; + } + + public SharedLog forSubComponent(String component) { + if (!isRootLogInstance()) { + component = mComponent + COMPONENT_DELIMITER + component; + } + return new SharedLog(mLocalLog, mTag, component); + } + + public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { + mLocalLog.readOnlyLocalLog().dump(fd, writer, args); + } + + ////// + // Methods that both log an entry and emit it to the system log. + ////// + + public void e(Exception e) { + Log.e(mTag, record(Category.ERROR, e.toString())); + } + + public void e(String msg) { + Log.e(mTag, record(Category.ERROR, msg)); + } + + public void i(String msg) { + Log.i(mTag, record(Category.NONE, msg)); + } + + public void w(String msg) { + Log.w(mTag, record(Category.WARN, msg)); + } + + ////// + // Methods that only log an entry (and do NOT emit to the system log). + ////// + + public void log(String msg) { + record(Category.NONE, msg); + } + + public void mark(String msg) { + record(Category.MARK, msg); + } + + private String record(Category category, String msg) { + final String entry = logLine(category, msg); + mLocalLog.log(entry); + return entry; + } + + private String logLine(Category category, String msg) { + final StringJoiner sj = new StringJoiner(" "); + if (!isRootLogInstance()) sj.add("[" + mComponent + "]"); + if (category != Category.NONE) sj.add(category.toString()); + return sj.add(msg).toString(); + } + + // Check whether this SharedLog instance is nominally the top level in + // a potential hierarchy of shared logs (the root of a tree), + // or is a subcomponent within the hierarchy. + private boolean isRootLogInstance() { + return TextUtils.isEmpty(mComponent) || mComponent.equals(mTag); + } +} diff --git a/tests/net/java/android/net/util/SharedLogTest.java b/tests/net/java/android/net/util/SharedLogTest.java new file mode 100644 index 000000000000..7fd7a634d298 --- /dev/null +++ b/tests/net/java/android/net/util/SharedLogTest.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2017 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.util; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.ByteArrayOutputStream; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Vector; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class SharedLogTest { + private static final String TIMESTAMP_PATTERN = + "^[0-9][0-9]-[0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9].[0-9][0-9][0-9]"; + private static final String TIMESTAMP = "mm-dd HH:MM:SS.xxx"; + + @Test + public void testBasicOperation() { + final SharedLog logTop = new SharedLog("top"); + logTop.mark("first post!"); + + final SharedLog logLevel2a = logTop.forSubComponent("twoA"); + final SharedLog logLevel2b = logTop.forSubComponent("twoB"); + logLevel2b.e("2b or not 2b"); + logLevel2a.w("second post?"); + + final SharedLog logLevel3 = logLevel2a.forSubComponent("three"); + logTop.log("still logging"); + logLevel3.log("3 >> 2"); + logLevel2a.mark("ok: last post"); + + final String[] expected = { + TIMESTAMP + " - MARK first post!", + TIMESTAMP + " - [twoB] ERROR 2b or not 2b", + TIMESTAMP + " - [twoA] WARN second post?", + TIMESTAMP + " - still logging", + TIMESTAMP + " - [twoA.three] 3 >> 2", + TIMESTAMP + " - [twoA] MARK ok: last post", + }; + // Verify the logs are all there and in the correct order. + verifyLogLines(expected, logTop); + + // In fact, because they all share the same underlying LocalLog, + // every subcomponent SharedLog's dump() is identical. + verifyLogLines(expected, logLevel2a); + verifyLogLines(expected, logLevel2b); + verifyLogLines(expected, logLevel3); + } + + private static void verifyLogLines(String[] expected, SharedLog log) { + final ByteArrayOutputStream ostream = new ByteArrayOutputStream(); + final PrintWriter pw = new PrintWriter(ostream, true); + log.dump(null, pw, null); + + final String dumpOutput = ostream.toString(); + assertTrue(dumpOutput != null); + assertTrue(!"".equals(dumpOutput)); + + final String[] lines = dumpOutput.split("\n"); + assertEquals(expected.length, lines.length); + + for (int i = 0; i < lines.length; i++) { + // Fix up the timestamps. + lines[i] = lines[i].replaceAll(TIMESTAMP_PATTERN, TIMESTAMP); + } + + for (int i = 0; i < expected.length; i++) { + assertEquals(expected[i], lines[i]); + } + } +} diff --git a/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java b/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java index a3f33dc13441..27e683c0881c 100644 --- a/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java +++ b/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java @@ -38,6 +38,7 @@ import static com.android.server.connectivity.tethering.IControlsTethering.STATE import android.net.ConnectivityManager; import android.net.INetworkStatsService; import android.net.InterfaceConfiguration; +import android.net.util.SharedLog; import android.os.INetworkManagementService; import android.os.RemoteException; import android.os.test.TestLooper; @@ -63,12 +64,14 @@ public class TetherInterfaceStateMachineTest { @Mock private IControlsTethering mTetherHelper; @Mock private InterfaceConfiguration mInterfaceConfiguration; @Mock private IPv6TetheringInterfaceServices mIPv6TetheringInterfaceServices; + @Mock private SharedLog mSharedLog; private final TestLooper mLooper = new TestLooper(); private TetherInterfaceStateMachine mTestedSm; private void initStateMachine(int interfaceType) throws Exception { - mTestedSm = new TetherInterfaceStateMachine(IFACE_NAME, mLooper.getLooper(), interfaceType, + mTestedSm = new TetherInterfaceStateMachine( + IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNMService, mStatsService, mTetherHelper, mIPv6TetheringInterfaceServices); mTestedSm.start(); // Starting the state machine always puts us in a consistent state and notifies @@ -90,12 +93,13 @@ public class TetherInterfaceStateMachineTest { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); + when(mSharedLog.forSubComponent(anyString())).thenReturn(mSharedLog); } @Test public void startsOutAvailable() { mTestedSm = new TetherInterfaceStateMachine(IFACE_NAME, mLooper.getLooper(), - TETHERING_BLUETOOTH, mNMService, mStatsService, mTetherHelper, + TETHERING_BLUETOOTH, mSharedLog, mNMService, mStatsService, mTetherHelper, mIPv6TetheringInterfaceServices); mTestedSm.start(); mLooper.dispatchAll(); diff --git a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java index c72efb0344d1..9bb392a23d56 100644 --- a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java +++ b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java @@ -25,11 +25,13 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; import android.content.Context; import android.os.Handler; @@ -40,6 +42,7 @@ import android.net.IConnectivityManager; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkRequest; +import android.net.util.SharedLog; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -69,6 +72,7 @@ public class UpstreamNetworkMonitorTest { @Mock private Context mContext; @Mock private IConnectivityManager mCS; + @Mock private SharedLog mLog; private TestStateMachine mSM; private TestConnectivityManager mCM; @@ -78,10 +82,12 @@ public class UpstreamNetworkMonitorTest { MockitoAnnotations.initMocks(this); reset(mContext); reset(mCS); + reset(mLog); + when(mLog.forSubComponent(anyString())).thenReturn(mLog); mCM = spy(new TestConnectivityManager(mContext, mCS)); mSM = new TestStateMachine(); - mUNM = new UpstreamNetworkMonitor(mSM, EVENT_UNM_UPDATE, (ConnectivityManager) mCM); + mUNM = new UpstreamNetworkMonitor(mSM, EVENT_UNM_UPDATE, (ConnectivityManager) mCM, mLog); } @After public void tearDown() throws Exception { |