diff options
author | Mukesh Agrawal <quiche@google.com> | 2016-04-06 20:07:38 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2016-04-06 20:07:39 +0000 |
commit | d1bd53369b88d91968c19dd0cc288d1d2db8b3c6 (patch) | |
tree | 1d9b3c124a5d0c8a3a02a8201f9bd3f4ff39cdc3 /service | |
parent | c1be2851f1c54e6e047ac62d71c9feb7e8df1561 (diff) | |
parent | 066cc9f845897357886081fbd972ab36e74ce345 (diff) |
Merge changes from topic 'packet-fate' into nyc-dev
* changes:
WifiStateMachine: report failures to WifiLogger
WifiLogger: add support for packet fate
WifiNative: flesh out packet fate implementation
WifiNative C++: add support for packet fate
WifiNative: add infrastructure for packet fates
jni_helper: add createObjectWithArgs
Diffstat (limited to 'service')
-rw-r--r-- | service/java/com/android/server/wifi/BaseWifiLogger.java | 4 | ||||
-rw-r--r-- | service/java/com/android/server/wifi/WifiLogger.java | 79 | ||||
-rw-r--r-- | service/java/com/android/server/wifi/WifiLoggerHal.java | 49 | ||||
-rw-r--r-- | service/java/com/android/server/wifi/WifiNative.java | 199 | ||||
-rw-r--r-- | service/java/com/android/server/wifi/WifiStateMachine.java | 42 | ||||
-rw-r--r-- | service/jni/com_android_server_wifi_WifiNative.cpp | 141 | ||||
-rw-r--r-- | service/jni/jni_helper.cpp | 21 | ||||
-rw-r--r-- | service/jni/jni_helper.h | 7 |
8 files changed, 518 insertions, 24 deletions
diff --git a/service/java/com/android/server/wifi/BaseWifiLogger.java b/service/java/com/android/server/wifi/BaseWifiLogger.java index 59a86ae69..43139d29c 100644 --- a/service/java/com/android/server/wifi/BaseWifiLogger.java +++ b/service/java/com/android/server/wifi/BaseWifiLogger.java @@ -28,6 +28,8 @@ public class BaseWifiLogger { public synchronized void stopLogging() { } + synchronized void reportConnectionFailure() {} + public synchronized void captureBugReportData(int reason) { } public synchronized void captureAlertData(int errorCode, byte[] alertData) { } @@ -38,7 +40,7 @@ public class BaseWifiLogger { pw.println("set config_wifi_enable_wifi_firmware_debugging to enable"); } - public synchronized void dump(PrintWriter pw) { + protected synchronized void dump(PrintWriter pw) { pw.println("Chipset information :-----------------------------------------------"); pw.println("FW Version is: " + mFirmwareVersion); pw.println("Driver Version is: " + mDriverVersion); diff --git a/service/java/com/android/server/wifi/WifiLogger.java b/service/java/com/android/server/wifi/WifiLogger.java index 0d358314d..64c570cd8 100644 --- a/service/java/com/android/server/wifi/WifiLogger.java +++ b/service/java/com/android/server/wifi/WifiLogger.java @@ -30,6 +30,8 @@ import java.io.InputStreamReader; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Calendar; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.zip.Deflater; @@ -116,6 +118,10 @@ class WifiLogger extends BaseWifiLogger { stopLoggingAllBuffers(); startLoggingAllExceptPerPacketBuffers(); } + + if (verboseEnabled && !mWifiNative.startPktFateMonitoring()) { + Log.e(TAG, "Failed to start packet fate monitoring"); + } } @Override @@ -153,6 +159,15 @@ class WifiLogger extends BaseWifiLogger { } @Override + synchronized void reportConnectionFailure() { + if (mLogLevel <= VERBOSE_NORMAL_LOG) { + return; + } + + mPacketFatesForLastFailure = fetchPacketFates(); + } + + @Override public synchronized void captureBugReportData(int reason) { BugReport report = captureBugreport(reason, true); mLastBugReports.addLast(report); @@ -167,7 +182,7 @@ class WifiLogger extends BaseWifiLogger { @Override public synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - dump(pw); + super.dump(pw); for (int i = 0; i < mLastAlerts.size(); i++) { pw.println("--------------------------------------------------------------------"); @@ -183,6 +198,8 @@ class WifiLogger extends BaseWifiLogger { pw.println("--------------------------------------------------------------------"); } + dumpPacketFates(pw); + pw.println("--------------------------------------------------------------------"); } @@ -521,4 +538,64 @@ class WifiLogger extends BaseWifiLogger { return lines; } + /** Packet fate reporting */ + private ArrayList<WifiNative.FateReport> mPacketFatesForLastFailure; + + private ArrayList<WifiNative.FateReport> fetchPacketFates() { + ArrayList<WifiNative.FateReport> mergedFates = new ArrayList<WifiNative.FateReport>(); + WifiNative.TxFateReport[] txFates = + new WifiNative.TxFateReport[WifiLoggerHal.MAX_FATE_LOG_LEN]; + if (mWifiNative.getTxPktFates(txFates)) { + for (int i = 0; i < txFates.length && txFates[i] != null; i++) { + mergedFates.add(txFates[i]); + } + } + + WifiNative.RxFateReport[] rxFates = + new WifiNative.RxFateReport[WifiLoggerHal.MAX_FATE_LOG_LEN]; + if (mWifiNative.getRxPktFates(rxFates)) { + for (int i = 0; i < rxFates.length && rxFates[i] != null; i++) { + mergedFates.add(rxFates[i]); + } + } + + Collections.sort(mergedFates, new Comparator<WifiNative.FateReport>() { + @Override + public int compare(WifiNative.FateReport lhs, WifiNative.FateReport rhs) { + return Long.compare(lhs.mDriverTimestampUSec, rhs.mDriverTimestampUSec); + } + }); + + return mergedFates; + } + + private void dumpPacketFates(PrintWriter pw) { + dumpPacketFatesInternal(pw, "Last failed connection fates", mPacketFatesForLastFailure); + if (DBG) { + dumpPacketFatesInternal(pw, "Latest fates", fetchPacketFates()); + } + } + + private static void dumpPacketFatesInternal( + PrintWriter pw, String description, ArrayList<WifiNative.FateReport> fates) { + if (fates == null) { + pw.format("No fates fetched for \"%s\"\n", description); + return; + } + + if (fates.size() == 0) { + pw.format("HAL provided zero fates for \"%s\"\n", description); + return; + } + + int i = 0; + pw.format("--------------------- %s ----------------------\n", description); + for (WifiNative.FateReport fate : fates) { + pw.format("Frame number: %d\n", i + 1); + pw.print(fate); + pw.print("\n"); + ++i; + } + pw.println("--------------------------------------------------------------------"); + } } diff --git a/service/java/com/android/server/wifi/WifiLoggerHal.java b/service/java/com/android/server/wifi/WifiLoggerHal.java new file mode 100644 index 000000000..1d542b87d --- /dev/null +++ b/service/java/com/android/server/wifi/WifiLoggerHal.java @@ -0,0 +1,49 @@ +/* + * 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.wifi; + +class WifiLoggerHal { + // Must match wifi_logger.h + static final int MAX_FATE_LOG_LEN = 32; + + static final byte FRAME_TYPE_UNKNOWN = 0; + static final byte FRAME_TYPE_ETHERNET_II = 1; + static final byte FRAME_TYPE_80211_MGMT = 2; + + static final byte TX_PKT_FATE_ACKED = 0; + static final byte TX_PKT_FATE_SENT = 1; + static final byte TX_PKT_FATE_FW_QUEUED = 2; + static final byte TX_PKT_FATE_FW_DROP_INVALID = 3; + static final byte TX_PKT_FATE_FW_DROP_NOBUFS = 4; + static final byte TX_PKT_FATE_FW_DROP_OTHER = 5; + static final byte TX_PKT_FATE_DRV_QUEUED = 6; + static final byte TX_PKT_FATE_DRV_DROP_INVALID = 7; + static final byte TX_PKT_FATE_DRV_DROP_NOBUFS = 9; + static final byte TX_PKT_FATE_DRV_DROP_OTHER = 10; + + static final byte RX_PKT_FATE_SUCCESS = 0; + static final byte RX_PKT_FATE_FW_QUEUED = 1; + static final byte RX_PKT_FATE_FW_DROP_FILTER = 2; + static final byte RX_PKT_FATE_FW_DROP_INVALID = 3; + static final byte RX_PKT_FATE_FW_DROP_NOBUFS = 4; + static final byte RX_PKT_FATE_FW_DROP_OTHER = 5; + static final byte RX_PKT_FATE_DRV_QUEUED = 6; + static final byte RX_PKT_FATE_DRV_DROP_FILTER = 7; + static final byte RX_PKT_FATE_DRV_DROP_INVALID = 8; + static final byte RX_PKT_FATE_DRV_DROP_NOBUFS = 9; + static final byte RX_PKT_FATE_DRV_DROP_OTHER = 10; +} diff --git a/service/java/com/android/server/wifi/WifiNative.java b/service/java/com/android/server/wifi/WifiNative.java index 2c659ba3e..bcd00f0a2 100644 --- a/service/java/com/android/server/wifi/WifiNative.java +++ b/service/java/com/android/server/wifi/WifiNative.java @@ -44,6 +44,8 @@ import android.text.TextUtils; import android.util.LocalLog; import android.util.Log; +import com.android.internal.annotations.Immutable; +import com.android.internal.util.HexDump; import com.android.server.connectivity.KeepalivePacketData; import com.android.server.wifi.hotspot2.NetworkDetail; import com.android.server.wifi.hotspot2.SupplicantBridge; @@ -55,6 +57,8 @@ import libcore.util.HexEncoding; import org.json.JSONException; import org.json.JSONObject; +import java.io.PrintWriter; +import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; @@ -85,6 +89,9 @@ import java.util.Set; public class WifiNative { private static boolean DBG = false; + // Must match wifi_hal.h + public static final int WIFI_SUCCESS = 0; + /** * Hold this lock before calling supplicant or HAL methods * it is required to mutually exclude access to the driver @@ -2772,6 +2779,198 @@ public class WifiNative { } //--------------------------------------------------------------------------------- + /* Packet fate API */ + + @Immutable + abstract static class FateReport { + final byte mFate; + final long mDriverTimestampUSec; + final byte mFrameType; + final byte[] mFrameBytes; + + FateReport(byte fate, long driverTimestampUSec, byte frameType, byte[] frameBytes) { + mFate = fate; + mDriverTimestampUSec = driverTimestampUSec; + mFrameType = frameType; + mFrameBytes = frameBytes; + } + + @Override + public String toString() { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + pw.format("Frame direction: %s\n", directionToString()); + pw.format("Frame timestamp: %d\n", mDriverTimestampUSec); + pw.format("Frame fate: %s\n", fateToString()); + pw.format("Frame type: %s\n", frameTypeToString(mFrameType)); + pw.format("Frame length: %d\n", mFrameBytes.length); + pw.append("Frame bytes"); + pw.append(HexDump.dumpHexString(mFrameBytes)); + pw.append("\n"); + return sw.toString(); + } + + protected abstract String directionToString(); + + protected abstract String fateToString(); + + private static String frameTypeToString(byte frameType) { + switch (frameType) { + case WifiLoggerHal.FRAME_TYPE_UNKNOWN: + return "unknown"; + case WifiLoggerHal.FRAME_TYPE_ETHERNET_II: + return "data"; + case WifiLoggerHal.FRAME_TYPE_80211_MGMT: + return "802.11 management"; + default: + return Byte.toString(frameType); + } + } + } + + /** + * Represents the fate information for one outbound packet. + */ + @Immutable + public static final class TxFateReport extends FateReport { + TxFateReport(byte fate, long driverTimestampUSec, byte frameType, byte[] frameBytes) { + super(fate, driverTimestampUSec, frameType, frameBytes); + } + + @Override + protected String directionToString() { + return "TX"; + } + + @Override + protected String fateToString() { + switch (mFate) { + case WifiLoggerHal.TX_PKT_FATE_ACKED: + return "acked"; + case WifiLoggerHal.TX_PKT_FATE_SENT: + return "sent"; + case WifiLoggerHal.TX_PKT_FATE_FW_QUEUED: + return "firmware queued"; + case WifiLoggerHal.TX_PKT_FATE_FW_DROP_INVALID: + return "firmware dropped (invalid frame)"; + case WifiLoggerHal.TX_PKT_FATE_FW_DROP_NOBUFS: + return "firmware dropped (no bufs)"; + case WifiLoggerHal.TX_PKT_FATE_FW_DROP_OTHER: + return "firmware dropped (other)"; + case WifiLoggerHal.TX_PKT_FATE_DRV_QUEUED: + return "driver queued"; + case WifiLoggerHal.TX_PKT_FATE_DRV_DROP_INVALID: + return "driver dropped (invalid frame)"; + case WifiLoggerHal.TX_PKT_FATE_DRV_DROP_NOBUFS: + return "driver dropped (no bufs)"; + case WifiLoggerHal.TX_PKT_FATE_DRV_DROP_OTHER: + return "driver dropped (other)"; + default: + return Byte.toString(mFate); + } + } + } + + /** + * Represents the fate information for one inbound packet. + */ + @Immutable + public static final class RxFateReport extends FateReport { + RxFateReport(byte fate, long driverTimestampUSec, byte frameType, byte[] frameBytes) { + super(fate, driverTimestampUSec, frameType, frameBytes); + } + + @Override + protected String directionToString() { + return "RX"; + } + + @Override + protected String fateToString() { + switch (mFate) { + case WifiLoggerHal.RX_PKT_FATE_SUCCESS: + return "success"; + case WifiLoggerHal.RX_PKT_FATE_FW_QUEUED: + return "firmware queued"; + case WifiLoggerHal.RX_PKT_FATE_FW_DROP_FILTER: + return "firmware dropped (filter)"; + case WifiLoggerHal.RX_PKT_FATE_FW_DROP_INVALID: + return "firmware dropped (invalid frame)"; + case WifiLoggerHal.RX_PKT_FATE_FW_DROP_NOBUFS: + return "firmware dropped (no bufs)"; + case WifiLoggerHal.RX_PKT_FATE_FW_DROP_OTHER: + return "firmware dropped (other)"; + case WifiLoggerHal.RX_PKT_FATE_DRV_QUEUED: + return "driver queued"; + case WifiLoggerHal.RX_PKT_FATE_DRV_DROP_FILTER: + return "driver dropped (filter)"; + case WifiLoggerHal.RX_PKT_FATE_DRV_DROP_INVALID: + return "driver dropped (invalid frame)"; + case WifiLoggerHal.RX_PKT_FATE_DRV_DROP_NOBUFS: + return "driver dropped (no bufs)"; + case WifiLoggerHal.RX_PKT_FATE_DRV_DROP_OTHER: + return "driver dropped (other)"; + default: + return Byte.toString(mFate); + } + } + } + + private static native int startPktFateMonitoringNative(int iface); + /** + * Ask the HAL to enable packet fate monitoring. Fails unless HAL is started. + */ + public boolean startPktFateMonitoring() { + synchronized (sLock) { + if (isHalStarted()) { + return startPktFateMonitoringNative(sWlan0Index) == WIFI_SUCCESS; + } else { + return false; + } + } + } + + private static native int getTxPktFatesNative(int iface, TxFateReport[] reportBufs); + /** + * Fetch the most recent TX packet fates from the HAL. Fails unless HAL is started. + */ + public boolean getTxPktFates(TxFateReport[] reportBufs) { + synchronized (sLock) { + if (isHalStarted()) { + int res = getTxPktFatesNative(sWlan0Index, reportBufs); + if (res != WIFI_SUCCESS) { + Log.e(TAG, "getTxPktFatesNative returned " + res); + return false; + } else { + return true; + } + } else { + return false; + } + } + } + + private static native int getRxPktFatesNative(int iface, RxFateReport[] reportBufs); + /** + * Fetch the most recent RX packet fates from the HAL. Fails unless HAL is started. + */ + public boolean getRxPktFates(RxFateReport[] reportBufs) { + synchronized (sLock) { + if (isHalStarted()) { + int res = getRxPktFatesNative(sWlan0Index, reportBufs); + if (res != WIFI_SUCCESS) { + Log.e(TAG, "getRxPktFatesNative returned " + res); + return false; + } else { + return true; + } + } else { + return false; + } + } + } + + //--------------------------------------------------------------------------------- /* Configure ePNO/PNO */ private static PnoEventHandler sPnoEventHandler; private static int sPnoCmdId = 0; diff --git a/service/java/com/android/server/wifi/WifiStateMachine.java b/service/java/com/android/server/wifi/WifiStateMachine.java index 1fa247645..b3e6d960a 100644 --- a/service/java/com/android/server/wifi/WifiStateMachine.java +++ b/service/java/com/android/server/wifi/WifiStateMachine.java @@ -4449,6 +4449,22 @@ public class WifiStateMachine extends StateMachine implements WifiNative.PnoEven mWifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE); } + /** + * Inform other components (WifiMetrics, WifiLogger, etc.) that the current connection attempt + * has concluded. + */ + private void reportConnectionAttemptEnd(int level2FailureCode, int connectivityFailureCode) { + mWifiMetrics.endConnectionEvent(level2FailureCode, connectivityFailureCode); + switch (level2FailureCode) { + case WifiMetrics.ConnectionEvent.FAILURE_NONE: + case WifiMetrics.ConnectionEvent.FAILURE_REDUNDANT_CONNECTION_ATTEMPT: + // WifiLogger doesn't care about success, or pre-empted connections. + break; + default: + mWifiLogger.reportConnectionFailure(); + } + } + private void handleIPv4Success(DhcpResults dhcpResults) { if (DBG) { logd("handleIPv4Success <" + dhcpResults.toString() + ">"); @@ -4518,7 +4534,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.PnoEven } log("DHCP failure count=" + count); } - mWifiMetrics.endConnectionEvent( + reportConnectionAttemptEnd( WifiMetrics.ConnectionEvent.FAILURE_DHCP, WifiMetricsProto.ConnectionEvent.HLF_DHCP); synchronized(mDhcpResultsLock) { @@ -6132,7 +6148,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.PnoEven mSupplicantStateTracker.sendMessage(WifiMonitor.ASSOCIATION_REJECTION_EVENT); //If rejection occurred while Metrics is tracking a ConnnectionEvent, end it. - mWifiMetrics.endConnectionEvent( + reportConnectionAttemptEnd( WifiMetrics.ConnectionEvent.FAILURE_ASSOCIATION_REJECTION, WifiMetricsProto.ConnectionEvent.HLF_NONE); break; @@ -6145,7 +6161,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.PnoEven .DISABLED_AUTHENTICATION_FAILURE); } //If failure occurred while Metrics is tracking a ConnnectionEvent, end it. - mWifiMetrics.endConnectionEvent( + reportConnectionAttemptEnd( WifiMetrics.ConnectionEvent.FAILURE_AUTHENTICATION_FAILURE, WifiMetricsProto.ConnectionEvent.HLF_NONE); break; @@ -6156,7 +6172,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.PnoEven message.arg1, WifiConfiguration.NetworkSelectionStatus .DISABLED_AUTHENTICATION_FAILURE); - mWifiMetrics.endConnectionEvent( + reportConnectionAttemptEnd( WifiMetrics.ConnectionEvent.FAILURE_SSID_TEMP_DISABLED, WifiMetricsProto.ConnectionEvent.HLF_NONE); break; @@ -6608,7 +6624,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.PnoEven loge("Failed to connect config: " + config + " netId: " + netId); replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED, WifiManager.ERROR); - mWifiMetrics.endConnectionEvent( + reportConnectionAttemptEnd( WifiMetrics.ConnectionEvent.FAILURE_CONNECT_NETWORK_FAILED, WifiMetricsProto.ConnectionEvent.HLF_NONE); break; @@ -6760,7 +6776,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.PnoEven loge("Failed to connect config: " + config + " netId: " + netId); replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED, WifiManager.ERROR); - mWifiMetrics.endConnectionEvent( + reportConnectionAttemptEnd( WifiMetrics.ConnectionEvent.FAILURE_CONNECT_NETWORK_FAILED, WifiMetricsProto.ConnectionEvent.HLF_NONE); break; @@ -7330,7 +7346,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.PnoEven } case CMD_IP_CONFIGURATION_SUCCESSFUL: handleSuccessfulIpConfiguration(); - mWifiMetrics.endConnectionEvent( + reportConnectionAttemptEnd( WifiMetrics.ConnectionEvent.FAILURE_NONE, WifiMetricsProto.ConnectionEvent.HLF_NONE); sendConnectedState(); @@ -7738,7 +7754,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.PnoEven /* Defer any power mode changes since we must keep active power mode at DHCP */ case WifiMonitor.NETWORK_DISCONNECTION_EVENT: - mWifiMetrics.endConnectionEvent( + reportConnectionAttemptEnd( WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION, WifiMetricsProto.ConnectionEvent.HLF_NONE); return NOT_HANDLED; @@ -7886,7 +7902,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.PnoEven sendNetworkStateChangeBroadcast(mLastBssid); // Successful framework roam! (probably) - mWifiMetrics.endConnectionEvent( + reportConnectionAttemptEnd( WifiMetrics.ConnectionEvent.FAILURE_NONE, WifiMetricsProto.ConnectionEvent.HLF_NONE); // We used to transition to ObtainingIpState in an @@ -8098,7 +8114,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.PnoEven return NOT_HANDLED; case WifiMonitor.NETWORK_DISCONNECTION_EVENT: long lastRoam = 0; - mWifiMetrics.endConnectionEvent( + reportConnectionAttemptEnd( WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION, WifiMetricsProto.ConnectionEvent.HLF_NONE); if (mLastDriverRoamAttempt != 0) { @@ -8211,7 +8227,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.PnoEven } if (deferForUserInput(message, netId, false)) { - mWifiMetrics.endConnectionEvent( + reportConnectionAttemptEnd( WifiMetrics.ConnectionEvent.FAILURE_CONNECT_NETWORK_FAILED, WifiMetricsProto.ConnectionEvent.HLF_NONE); break; @@ -8219,7 +8235,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.PnoEven WifiConfiguration.USER_BANNED) { replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED, WifiManager.NOT_AUTHORIZED); - mWifiMetrics.endConnectionEvent( + reportConnectionAttemptEnd( WifiMetrics.ConnectionEvent.FAILURE_CONNECT_NETWORK_FAILED, WifiMetricsProto.ConnectionEvent.HLF_NONE); break; @@ -8247,7 +8263,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.PnoEven replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED, WifiManager.ERROR); messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL; - mWifiMetrics.endConnectionEvent( + reportConnectionAttemptEnd( WifiMetrics.ConnectionEvent.FAILURE_CONNECT_NETWORK_FAILED, WifiMetricsProto.ConnectionEvent.HLF_NONE); break; diff --git a/service/jni/com_android_server_wifi_WifiNative.cpp b/service/jni/com_android_server_wifi_WifiNative.cpp index 20c5b70e9..6dbb6064c 100644 --- a/service/jni/com_android_server_wifi_WifiNative.cpp +++ b/service/jni/com_android_server_wifi_WifiNative.cpp @@ -29,6 +29,11 @@ #include <sys/klog.h> #include <linux/if.h> #include <linux/if_arp.h> + +#include <algorithm> +#include <limits> +#include <vector> + #include "wifi.h" #include "wifi_hal.h" #include "jni_helper.h" @@ -1919,6 +1924,137 @@ static jboolean android_net_wifi_reset_log_handler(JNIEnv *env, jclass cls, jint return true; } +static jint android_net_wifi_start_pkt_fate_monitoring(JNIEnv *env, jclass cls, jint iface) { + + JNIHelper helper(env); + return hal_fn.wifi_start_pkt_fate_monitoring( + getIfaceHandle(helper, cls, iface)); +} + +// Helper for make_default_fate(). +template<typename T> void set_to_max(T* value) { + if (!value) { + return; + } + *value = std::numeric_limits<T>::max(); +} + +// make_default_fate() has two purposes: +// 1) Minimize the chances of data leakage. In case the HAL gives us an overlong long |frame_len|, +// for example, we want to return zeros, rather than other data from this process. +// 2) Make it obvious when the HAL doesn't set a field. We accomplish this by setting fields +// to "impossible" values, where possible. +// Normally, such work would be done in a ctor. However, doing so would make the HAL API +// incompatible with C. So we use a free-standing function instead. +// +// TODO(quiche): Add unit test for this function. b/27726696 +template<typename FateReportT> FateReportT make_default_fate() { + + FateReportT fate_report; + set_to_max(&fate_report.fate); + std::fill(std::begin(fate_report.md5_prefix), std::end(fate_report.md5_prefix), 0); + set_to_max(&fate_report.frame_inf.payload_type); + fate_report.frame_inf.frame_len = 0; + fate_report.frame_inf.driver_timestamp_usec = 0; + fate_report.frame_inf.firmware_timestamp_usec = 0; + std::fill(std::begin(fate_report.frame_inf.frame_content.ieee_80211_mgmt_bytes), + std::end(fate_report.frame_inf.frame_content.ieee_80211_mgmt_bytes), 0); + return fate_report; +} + +// TODO(quiche): Add unit test for this function. b/27726696 +template<typename FateReportT, typename HalFateFetcherT> wifi_error get_pkt_fates( + HalFateFetcherT fate_fetcher_func, const char *java_fate_type, + JNIEnv *env, jclass cls, jint iface, jobjectArray reports) { + + JNIHelper helper(env); + const size_t n_reports_wanted = + std::min(helper.getArrayLength(reports), MAX_FATE_LOG_LEN); + + std::vector<FateReportT> report_bufs(n_reports_wanted, make_default_fate<FateReportT>()); + size_t n_reports_provided = 0; + wifi_error result = fate_fetcher_func( + getIfaceHandle(helper, cls, iface), + report_bufs.data(), + n_reports_wanted, + &n_reports_provided); + if (result != WIFI_SUCCESS) { + return result; + } + + if (n_reports_provided > n_reports_wanted) { + LOG_ALWAYS_FATAL( + "HAL data exceeds request; memory may be corrupt (provided: %zu, requested: %zu)", + n_reports_provided, n_reports_wanted); + } + + for (size_t i = 0; i < n_reports_provided; ++i) { + const FateReportT& report(report_bufs[i]); + + const char *frame_bytes_native = nullptr; + size_t max_frame_len; + switch (report.frame_inf.payload_type) { + case FRAME_TYPE_UNKNOWN: + case FRAME_TYPE_ETHERNET_II: + max_frame_len = MAX_FRAME_LEN_ETHERNET; + frame_bytes_native = report.frame_inf.frame_content.ethernet_ii_bytes; + break; + case FRAME_TYPE_80211_MGMT: + max_frame_len = MAX_FRAME_LEN_80211_MGMT; + frame_bytes_native = report.frame_inf.frame_content.ieee_80211_mgmt_bytes; + break; + default: + max_frame_len = 0; + frame_bytes_native = 0; + } + + size_t copy_len = report.frame_inf.frame_len; + if (copy_len > max_frame_len) { + ALOGW("Overly long frame (len: %zu, max: %zu)", copy_len, max_frame_len); + copy_len = max_frame_len; + } + + JNIObject<jbyteArray> frame_bytes_java = helper.newByteArray(copy_len); + if (frame_bytes_java.isNull()) { + ALOGE("Failed to allocate frame data buffer"); + return WIFI_ERROR_OUT_OF_MEMORY; + } + helper.setByteArrayRegion(frame_bytes_java, 0, copy_len, + reinterpret_cast<const jbyte *>(frame_bytes_native)); + + JNIObject<jobject> fate_report = helper.createObjectWithArgs( + java_fate_type, + "(BJB[B)V", // byte, long, byte, byte array + static_cast<jbyte>(report.fate), + static_cast<jlong>(report.frame_inf.driver_timestamp_usec), + static_cast<jbyte>(report.frame_inf.payload_type), + frame_bytes_java.get()); + if (fate_report.isNull()) { + ALOGE("Failed to create %s", java_fate_type); + return WIFI_ERROR_OUT_OF_MEMORY; + } + helper.setObjectArrayElement(reports, i, fate_report); + } + + return result; +} + +static jint android_net_wifi_get_tx_pkt_fates(JNIEnv *env, jclass cls, jint iface, + jobjectArray reports) { + + return get_pkt_fates<wifi_tx_report>( + hal_fn.wifi_get_tx_pkt_fates, "com/android/server/wifi/WifiNative$TxFateReport", + env, cls, iface, reports); +} + +static jint android_net_wifi_get_rx_pkt_fates(JNIEnv *env, jclass cls, jint iface, + jobjectArray reports) { + + return get_pkt_fates<wifi_rx_report>( + hal_fn.wifi_get_rx_pkt_fates, "com/android/server/wifi/WifiNative$RxFateReport", + env, cls, iface, reports); +} + // ---------------------------------------------------------------------------- // ePno framework // ---------------------------------------------------------------------------- @@ -2412,6 +2548,11 @@ static JNINativeMethod gWifiMethods[] = { (void*)android_net_wifi_setBssidBlacklist}, {"setLoggingEventHandlerNative", "(II)Z", (void *) android_net_wifi_set_log_handler}, {"resetLogHandlerNative", "(II)Z", (void *) android_net_wifi_reset_log_handler}, + {"startPktFateMonitoringNative", "(I)I", (void*) android_net_wifi_start_pkt_fate_monitoring}, + {"getTxPktFatesNative", "(I[Lcom/android/server/wifi/WifiNative$TxFateReport;)I", + (void*) android_net_wifi_get_tx_pkt_fates}, + {"getRxPktFatesNative", "(I[Lcom/android/server/wifi/WifiNative$RxFateReport;)I", + (void*) android_net_wifi_get_rx_pkt_fates}, { "startSendingOffloadedPacketNative", "(II[B[B[BI)I", (void*)android_net_wifi_start_sending_offloaded_packet}, { "stopSendingOffloadedPacketNative", "(II)I", diff --git a/service/jni/jni_helper.cpp b/service/jni/jni_helper.cpp index b4bf44cc2..c9b4edd28 100644 --- a/service/jni/jni_helper.cpp +++ b/service/jni/jni_helper.cpp @@ -569,26 +569,35 @@ jboolean JNIHelper::callStaticMethod(jclass cls, const char *method, const char return result; } -JNIObject<jobject> JNIHelper::createObject(const char *className) +JNIObject<jobject> JNIHelper::createObject(const char *className) { + return createObjectWithArgs(className, "()V"); +} + +JNIObject<jobject> JNIHelper::createObjectWithArgs( + const char *className, const char *signature, ...) { + va_list params; + va_start(params, signature); + JNIObject<jclass> cls(*this, mEnv->FindClass(className)); if (cls == NULL) { ALOGE("Error in finding class %s", className); return JNIObject<jobject>(*this, NULL); } - jmethodID constructor = mEnv->GetMethodID(cls, "<init>", "()V"); + jmethodID constructor = mEnv->GetMethodID(cls, "<init>", signature); if (constructor == 0) { ALOGE("Error in constructor ID for %s", className); return JNIObject<jobject>(*this, NULL); } - JNIObject<jobject> obj(*this, mEnv->NewObject(cls, constructor)); + JNIObject<jobject> obj(*this, mEnv->NewObjectV(cls, constructor, params)); if (obj == NULL) { ALOGE("Could not create new object of %s", className); return JNIObject<jobject>(*this, NULL); } + va_end(params); return obj; } @@ -653,15 +662,15 @@ void JNIHelper::setObjectArrayElement(jobjectArray array, int index, jobject obj mEnv->SetObjectArrayElement(array, index, obj); } -void JNIHelper::setByteArrayRegion(jbyteArray array, int from, int to, jbyte *bytes) { +void JNIHelper::setByteArrayRegion(jbyteArray array, int from, int to, const jbyte *bytes) { mEnv->SetByteArrayRegion(array, from, to, bytes); } -void JNIHelper::setIntArrayRegion(jintArray array, int from, int to, jint *ints) { +void JNIHelper::setIntArrayRegion(jintArray array, int from, int to, const jint *ints) { mEnv->SetIntArrayRegion(array, from, to, ints); } -void JNIHelper::setLongArrayRegion(jlongArray array, int from, int to, jlong *longs) { +void JNIHelper::setLongArrayRegion(jlongArray array, int from, int to, const jlong *longs) { mEnv->SetLongArrayRegion(array, from, to, longs); } diff --git a/service/jni/jni_helper.h b/service/jni/jni_helper.h index 80f51322d..bb65f1c27 100644 --- a/service/jni/jni_helper.h +++ b/service/jni/jni_helper.h @@ -96,6 +96,7 @@ public : jboolean setStringField(jobject obj, const char *name, const char *value); void reportEvent(jclass cls, const char *method, const char *signature, ...); JNIObject<jobject> createObject(const char *className); + JNIObject<jobject> createObjectWithArgs(const char *className, const char *signature, ...); JNIObject<jobjectArray> createObjectArray(const char *className, int size); void setObjectField(jobject obj, const char *name, const char *type, jobject value); void callMethod(jobject obj, const char *method, const char *signature, ...); @@ -120,9 +121,9 @@ public : JNIObject<jlongArray> newLongArray(int num); JNIObject<jstring> newStringUTF(const char *utf); void setObjectArrayElement(jobjectArray array, int index, jobject obj); - void setByteArrayRegion(jbyteArray array, int from, int to, jbyte *bytes); - void setIntArrayRegion(jintArray array, int from, int to, jint *ints); - void setLongArrayRegion(jlongArray array, int from, int to, jlong *longs); + void setByteArrayRegion(jbyteArray array, int from, int to, const jbyte *bytes); + void setIntArrayRegion(jintArray array, int from, int to, const jint *ints); + void setLongArrayRegion(jlongArray array, int from, int to, const jlong *longs); jobject newGlobalRef(jobject obj); void deleteGlobalRef(jobject obj); |