Loading common/networkstackclient/src/android/net/IpMemoryStoreClient.java +10 −4 Original line number Diff line number Diff line Loading @@ -92,6 +92,7 @@ public abstract class IpMemoryStoreClient { () -> service.storeNetworkAttributes(l2Key, attributes.toParcelable(), OnStatusListener.toAIDL(listener)))); } catch (ExecutionException m) { if (null == listener) return; ignoringRemoteException("Error storing network attributes", () -> listener.onComplete(new Status(Status.ERROR_UNKNOWN))); } Loading @@ -116,6 +117,7 @@ public abstract class IpMemoryStoreClient { () -> service.storeBlob(l2Key, clientId, name, data, OnStatusListener.toAIDL(listener)))); } catch (ExecutionException m) { if (null == listener) return; ignoringRemoteException("Error storing blob", () -> listener.onComplete(new Status(Status.ERROR_UNKNOWN))); } Loading Loading @@ -143,7 +145,8 @@ public abstract class IpMemoryStoreClient { OnL2KeyResponseListener.toAIDL(listener)))); } catch (ExecutionException m) { ignoringRemoteException("Error finding L2 Key", () -> listener.onL2KeyResponse(new Status(Status.ERROR_UNKNOWN), null)); () -> listener.onL2KeyResponse(new Status(Status.ERROR_UNKNOWN), null /* l2Key */)); } } Loading @@ -164,7 +167,8 @@ public abstract class IpMemoryStoreClient { OnSameL3NetworkResponseListener.toAIDL(listener)))); } catch (ExecutionException m) { ignoringRemoteException("Error checking for network sameness", () -> listener.onSameL3NetworkResponse(new Status(Status.ERROR_UNKNOWN), null)); () -> listener.onSameL3NetworkResponse(new Status(Status.ERROR_UNKNOWN), null /* response */)); } } Loading @@ -186,7 +190,7 @@ public abstract class IpMemoryStoreClient { } catch (ExecutionException m) { ignoringRemoteException("Error retrieving network attributes", () -> listener.onNetworkAttributesRetrieved(new Status(Status.ERROR_UNKNOWN), null, null)); null /* l2Key */, null /* attributes */)); } } Loading @@ -210,7 +214,7 @@ public abstract class IpMemoryStoreClient { } catch (ExecutionException m) { ignoringRemoteException("Error retrieving blob", () -> listener.onBlobRetrieved(new Status(Status.ERROR_UNKNOWN), null, null, null)); null /* l2Key */, null /* name */, null /* blob */)); } } Loading @@ -235,6 +239,7 @@ public abstract class IpMemoryStoreClient { runWhenServiceReady(service -> ignoringRemoteException(() -> service.delete(l2Key, needWipe, OnDeleteStatusListener.toAIDL(listener)))); } catch (ExecutionException m) { if (null == listener) return; ignoringRemoteException("Error deleting from the memory store", () -> listener.onComplete(new Status(Status.ERROR_UNKNOWN), 0 /* deletedRecords */)); Loading Loading @@ -266,6 +271,7 @@ public abstract class IpMemoryStoreClient { () -> service.deleteCluster(cluster, needWipe, OnDeleteStatusListener.toAIDL(listener)))); } catch (ExecutionException m) { if (null == listener) return; ignoringRemoteException("Error deleting from the memory store", () -> listener.onComplete(new Status(Status.ERROR_UNKNOWN), 0 /* deletedRecords */)); Loading src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreDatabase.java +59 −67 Original line number Diff line number Diff line Loading @@ -335,21 +335,19 @@ public class IpMemoryStoreDatabase { // Returns the expiry date of the specified row, or one of the error codes above if the // row is not found or some other error static long getExpiry(@NonNull final SQLiteDatabase db, @NonNull final String key) { final Cursor cursor = db.query(NetworkAttributesContract.TABLENAME, try (Cursor cursor = db.query(NetworkAttributesContract.TABLENAME, EXPIRY_COLUMN, // columns SELECT_L2KEY, // selection new String[] { key }, // selectionArgs null, // groupBy null, // having null // orderBy ); null)) { // orderBy // L2KEY is the primary key ; it should not be possible to get more than one // result here. 0 results means the key was not found. if (cursor.getCount() != 1) return EXPIRY_ERROR; cursor.moveToFirst(); final long result = cursor.getLong(0); // index in the EXPIRY_COLUMN array cursor.close(); return result; return cursor.getLong(0); // index in the EXPIRY_COLUMN array } } static final int RELEVANCE_ERROR = -1; // Legal values for relevance are positive Loading Loading @@ -399,20 +397,19 @@ public class IpMemoryStoreDatabase { @Nullable static NetworkAttributes retrieveNetworkAttributes(@NonNull final SQLiteDatabase db, @NonNull final String key) { final Cursor cursor = db.query(NetworkAttributesContract.TABLENAME, try (Cursor cursor = db.query(NetworkAttributesContract.TABLENAME, null, // columns, null means everything NetworkAttributesContract.COLNAME_L2KEY + " = ?", // selection new String[] { key }, // selectionArgs null, // groupBy null, // having null); // orderBy null)) { // orderBy // L2KEY is the primary key ; it should not be possible to get more than one // result here. 0 results means the key was not found. if (cursor.getCount() != 1) return null; cursor.moveToFirst(); final NetworkAttributes attributes = readNetworkAttributesLine(cursor); cursor.close(); return attributes; return readNetworkAttributesLine(cursor); } } private static final String[] DATA_COLUMN = new String[] { Loading @@ -422,7 +419,7 @@ public class IpMemoryStoreDatabase { @Nullable static byte[] retrieveBlob(@NonNull final SQLiteDatabase db, @NonNull final String key, @NonNull final String clientId, @NonNull final String name) { final Cursor cursor = db.query(PrivateDataContract.TABLENAME, try (Cursor cursor = db.query(PrivateDataContract.TABLENAME, DATA_COLUMN, // columns PrivateDataContract.COLNAME_L2KEY + " = ? AND " // selection + PrivateDataContract.COLNAME_CLIENT + " = ? AND " Loading @@ -430,14 +427,13 @@ public class IpMemoryStoreDatabase { new String[] { key, clientId, name }, // selectionArgs null, // groupBy null, // having null); // orderBy // The query above is querying by (composite) primary key, so it should not be possible to // get more than one result here. 0 results means the key was not found. null)) { // orderBy // The query above is querying by (composite) primary key, so it should not be possible // to get more than one result here. 0 results means the key was not found. if (cursor.getCount() != 1) return null; cursor.moveToFirst(); final byte[] result = cursor.getBlob(0); // index in the DATA_COLUMN array cursor.close(); return result; return cursor.getBlob(0); // index in the DATA_COLUMN array } } /** Loading @@ -449,7 +445,7 @@ public class IpMemoryStoreDatabase { try { db.delete(NetworkAttributesContract.TABLENAME, null, null); db.delete(PrivateDataContract.TABLENAME, null, null); final Cursor cursorNetworkAttributes = db.query( try (Cursor cursorNetworkAttributes = db.query( // table name NetworkAttributesContract.TABLENAME, // column name Loading @@ -459,13 +455,10 @@ public class IpMemoryStoreDatabase { null, // groupBy null, // having null, // orderBy "1"); // limit if (0 != cursorNetworkAttributes.getCount()) { cursorNetworkAttributes.close(); continue; "1")) { // limit if (0 != cursorNetworkAttributes.getCount()) continue; } cursorNetworkAttributes.close(); final Cursor cursorPrivateData = db.query( try (Cursor cursorPrivateData = db.query( // table name PrivateDataContract.TABLENAME, // column name Loading @@ -475,14 +468,10 @@ public class IpMemoryStoreDatabase { null, // groupBy null, // having null, // orderBy "1"); // limit if (0 != cursorPrivateData.getCount()) { cursorPrivateData.close(); continue; "1")) { // limit if (0 != cursorPrivateData.getCount()) continue; } cursorPrivateData.close(); db.setTransactionSuccessful(); return; } catch (SQLiteException e) { Log.e(TAG, "Could not wipe the data in database", e); } finally { Loading Loading @@ -575,7 +564,7 @@ public class IpMemoryStoreDatabase { final String selection = NetworkAttributesContract.COLNAME_EXPIRYDATE + " > ? AND (" + sj.toString() + ")"; final Cursor cursor = db.queryWithFactory(new CustomCursorFactory(args), try (Cursor cursor = db.queryWithFactory(new CustomCursorFactory(args), false, // distinct NetworkAttributesContract.TABLENAME, null, // columns, null means everything Loading @@ -584,11 +573,12 @@ public class IpMemoryStoreDatabase { null, // groupBy null, // having null, // orderBy null); // limit null)) { // limit if (cursor.getCount() <= 0) return null; cursor.moveToFirst(); String bestKey = null; float bestMatchConfidence = GROUPCLOSE_CONFIDENCE; // Never return a match worse than this. float bestMatchConfidence = GROUPCLOSE_CONFIDENCE; // Never return a match worse than this. while (!cursor.isAfterLast()) { final NetworkAttributes read = readNetworkAttributesLine(cursor); final float confidence = read.getNetworkGroupSamenessConfidence(attr); Loading @@ -598,9 +588,9 @@ public class IpMemoryStoreDatabase { } cursor.moveToNext(); } cursor.close(); return bestKey; } } /** * Delete a single entry by key. Loading Loading @@ -695,20 +685,21 @@ public class IpMemoryStoreDatabase { } // Queries number of NetworkAttributes that start from the lowest expiryDate. final Cursor cursor = db.query(NetworkAttributesContract.TABLENAME, final long expiryDate; try (Cursor cursor = db.query(NetworkAttributesContract.TABLENAME, new String[] {NetworkAttributesContract.COLNAME_EXPIRYDATE}, // columns null, // selection null, // selectionArgs null, // groupBy null, // having NetworkAttributesContract.COLNAME_EXPIRYDATE, // orderBy Integer.toString(number)); // limit Integer.toString(number))) { // limit if (cursor == null || cursor.getCount() <= 0) return Status.ERROR_GENERIC; cursor.moveToLast(); // Get the expiryDate from last record. final long expiryDate = getLong(cursor, NetworkAttributesContract.COLNAME_EXPIRYDATE, 0); cursor.close(); expiryDate = getLong(cursor, NetworkAttributesContract.COLNAME_EXPIRYDATE, 0); } db.beginTransaction(); try { Loading Loading @@ -736,16 +727,17 @@ public class IpMemoryStoreDatabase { static int getTotalRecordNumber(@NonNull final SQLiteDatabase db) { // Query the total number of NetworkAttributes final Cursor cursor = db.query(NetworkAttributesContract.TABLENAME, try (Cursor cursor = db.query(NetworkAttributesContract.TABLENAME, new String[] {"COUNT(*)"}, // columns null, // selection null, // selectionArgs null, // groupBy null, // having null); // orderBy null)) { // orderBy cursor.moveToFirst(); return cursor == null ? 0 : cursor.getInt(0); } } // Helper methods private static String getString(final Cursor cursor, final String columnName) { Loading tests/unit/src/com/android/networkstack/metrics/DataStallStatsUtilsTest.kt +73 −1 Original line number Diff line number Diff line Loading @@ -16,18 +16,33 @@ package com.android.networkstack.metrics import android.net.NetworkCapabilities.TRANSPORT_CELLULAR import android.net.captiveportal.CaptivePortalProbeResult import android.net.metrics.ValidationProbeEvent import android.net.util.DataStallUtils.DATA_STALL_EVALUATION_TYPE_DNS import android.telephony.TelephonyManager import androidx.test.filters.SmallTest import androidx.test.runner.AndroidJUnit4 import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession import com.android.dx.mockito.inline.extended.ExtendedMockito.verify import com.android.server.connectivity.nano.CellularData import com.android.server.connectivity.nano.DataStallEventProto import com.android.server.connectivity.nano.DnsEvent import com.google.protobuf.nano.MessageNano import java.util.Arrays import org.junit.Assert.assertEquals import org.junit.Test import org.junit.runner.RunWith import android.net.metrics.ValidationProbeEvent import org.mockito.ArgumentMatchers.eq @RunWith(AndroidJUnit4::class) @SmallTest class DataStallStatsUtilsTest { private val TEST_ELAPSED_TIME_MS = 123456789L private val TEST_MCCMNC = "123456" private val TEST_SIGNAL_STRENGTH = -100 private val RETURN_CODE_DNS_TIMEOUT = 255 @Test fun testProbeResultToEnum() { assertEquals(DataStallStatsUtils.probeResultToEnum(null), DataStallEventProto.INVALID) Loading @@ -53,4 +68,61 @@ class DataStallStatsUtilsTest { CaptivePortalProbeResult.PORTAL_CODE, ValidationProbeEvent.PROBE_HTTPS)), DataStallEventProto.PORTAL) } @Test fun testWrite() { val session = mockitoSession().spyStatic(NetworkStackStatsLog::class.java).startMocking() val stats = DataStallDetectionStats.Builder() .setEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS) .setNetworkType(TRANSPORT_CELLULAR) .setCellData(TelephonyManager.NETWORK_TYPE_LTE /* radioType */, true /* roaming */, TEST_MCCMNC /* networkMccmnc */, TEST_MCCMNC /* simMccmnc */, TEST_SIGNAL_STRENGTH /* signalStrength */) .setTcpFailRate(90) .setTcpSentSinceLastRecv(10) generateTimeoutDnsEvent(stats, count = 5) DataStallStatsUtils.write(stats.build(), CaptivePortalProbeResult.PARTIAL) verify { NetworkStackStatsLog.write( eq(NetworkStackStatsLog.DATA_STALL_EVENT), eq(DATA_STALL_EVALUATION_TYPE_DNS), eq(DataStallEventProto.PARTIAL), eq(TRANSPORT_CELLULAR), eq(DataStallDetectionStats.emptyWifiInfoIfNull(null)), eq(makeTestCellDataNano()), eq(makeTestDnsTimeoutNano(5)), eq(90) /* tcpFailRate */, eq(10) /* tcpSentSinceLastRecv */) } session.finishMocking() } private fun makeTestDnsTimeoutNano(timeoutCount: Int): ByteArray? { // Make an expected nano dns message. val event = DnsEvent() event.dnsReturnCode = IntArray(timeoutCount) event.dnsTime = LongArray(timeoutCount) Arrays.fill(event.dnsReturnCode, RETURN_CODE_DNS_TIMEOUT) Arrays.fill(event.dnsTime, TEST_ELAPSED_TIME_MS) return MessageNano.toByteArray(event) } private fun makeTestCellDataNano(): ByteArray? { // Make an expected nano cell data message. val data = CellularData() data.ratType = DataStallEventProto.RADIO_TECHNOLOGY_LTE data.networkMccmnc = TEST_MCCMNC data.simMccmnc = TEST_MCCMNC data.isRoaming = true data.signalStrength = TEST_SIGNAL_STRENGTH return MessageNano.toByteArray(data) } private fun generateTimeoutDnsEvent(stats: DataStallDetectionStats.Builder, count: Int) { repeat(count) { stats.addDnsEvent(RETURN_CODE_DNS_TIMEOUT, TEST_ELAPSED_TIME_MS) } } } Loading
common/networkstackclient/src/android/net/IpMemoryStoreClient.java +10 −4 Original line number Diff line number Diff line Loading @@ -92,6 +92,7 @@ public abstract class IpMemoryStoreClient { () -> service.storeNetworkAttributes(l2Key, attributes.toParcelable(), OnStatusListener.toAIDL(listener)))); } catch (ExecutionException m) { if (null == listener) return; ignoringRemoteException("Error storing network attributes", () -> listener.onComplete(new Status(Status.ERROR_UNKNOWN))); } Loading @@ -116,6 +117,7 @@ public abstract class IpMemoryStoreClient { () -> service.storeBlob(l2Key, clientId, name, data, OnStatusListener.toAIDL(listener)))); } catch (ExecutionException m) { if (null == listener) return; ignoringRemoteException("Error storing blob", () -> listener.onComplete(new Status(Status.ERROR_UNKNOWN))); } Loading Loading @@ -143,7 +145,8 @@ public abstract class IpMemoryStoreClient { OnL2KeyResponseListener.toAIDL(listener)))); } catch (ExecutionException m) { ignoringRemoteException("Error finding L2 Key", () -> listener.onL2KeyResponse(new Status(Status.ERROR_UNKNOWN), null)); () -> listener.onL2KeyResponse(new Status(Status.ERROR_UNKNOWN), null /* l2Key */)); } } Loading @@ -164,7 +167,8 @@ public abstract class IpMemoryStoreClient { OnSameL3NetworkResponseListener.toAIDL(listener)))); } catch (ExecutionException m) { ignoringRemoteException("Error checking for network sameness", () -> listener.onSameL3NetworkResponse(new Status(Status.ERROR_UNKNOWN), null)); () -> listener.onSameL3NetworkResponse(new Status(Status.ERROR_UNKNOWN), null /* response */)); } } Loading @@ -186,7 +190,7 @@ public abstract class IpMemoryStoreClient { } catch (ExecutionException m) { ignoringRemoteException("Error retrieving network attributes", () -> listener.onNetworkAttributesRetrieved(new Status(Status.ERROR_UNKNOWN), null, null)); null /* l2Key */, null /* attributes */)); } } Loading @@ -210,7 +214,7 @@ public abstract class IpMemoryStoreClient { } catch (ExecutionException m) { ignoringRemoteException("Error retrieving blob", () -> listener.onBlobRetrieved(new Status(Status.ERROR_UNKNOWN), null, null, null)); null /* l2Key */, null /* name */, null /* blob */)); } } Loading @@ -235,6 +239,7 @@ public abstract class IpMemoryStoreClient { runWhenServiceReady(service -> ignoringRemoteException(() -> service.delete(l2Key, needWipe, OnDeleteStatusListener.toAIDL(listener)))); } catch (ExecutionException m) { if (null == listener) return; ignoringRemoteException("Error deleting from the memory store", () -> listener.onComplete(new Status(Status.ERROR_UNKNOWN), 0 /* deletedRecords */)); Loading Loading @@ -266,6 +271,7 @@ public abstract class IpMemoryStoreClient { () -> service.deleteCluster(cluster, needWipe, OnDeleteStatusListener.toAIDL(listener)))); } catch (ExecutionException m) { if (null == listener) return; ignoringRemoteException("Error deleting from the memory store", () -> listener.onComplete(new Status(Status.ERROR_UNKNOWN), 0 /* deletedRecords */)); Loading
src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreDatabase.java +59 −67 Original line number Diff line number Diff line Loading @@ -335,21 +335,19 @@ public class IpMemoryStoreDatabase { // Returns the expiry date of the specified row, or one of the error codes above if the // row is not found or some other error static long getExpiry(@NonNull final SQLiteDatabase db, @NonNull final String key) { final Cursor cursor = db.query(NetworkAttributesContract.TABLENAME, try (Cursor cursor = db.query(NetworkAttributesContract.TABLENAME, EXPIRY_COLUMN, // columns SELECT_L2KEY, // selection new String[] { key }, // selectionArgs null, // groupBy null, // having null // orderBy ); null)) { // orderBy // L2KEY is the primary key ; it should not be possible to get more than one // result here. 0 results means the key was not found. if (cursor.getCount() != 1) return EXPIRY_ERROR; cursor.moveToFirst(); final long result = cursor.getLong(0); // index in the EXPIRY_COLUMN array cursor.close(); return result; return cursor.getLong(0); // index in the EXPIRY_COLUMN array } } static final int RELEVANCE_ERROR = -1; // Legal values for relevance are positive Loading Loading @@ -399,20 +397,19 @@ public class IpMemoryStoreDatabase { @Nullable static NetworkAttributes retrieveNetworkAttributes(@NonNull final SQLiteDatabase db, @NonNull final String key) { final Cursor cursor = db.query(NetworkAttributesContract.TABLENAME, try (Cursor cursor = db.query(NetworkAttributesContract.TABLENAME, null, // columns, null means everything NetworkAttributesContract.COLNAME_L2KEY + " = ?", // selection new String[] { key }, // selectionArgs null, // groupBy null, // having null); // orderBy null)) { // orderBy // L2KEY is the primary key ; it should not be possible to get more than one // result here. 0 results means the key was not found. if (cursor.getCount() != 1) return null; cursor.moveToFirst(); final NetworkAttributes attributes = readNetworkAttributesLine(cursor); cursor.close(); return attributes; return readNetworkAttributesLine(cursor); } } private static final String[] DATA_COLUMN = new String[] { Loading @@ -422,7 +419,7 @@ public class IpMemoryStoreDatabase { @Nullable static byte[] retrieveBlob(@NonNull final SQLiteDatabase db, @NonNull final String key, @NonNull final String clientId, @NonNull final String name) { final Cursor cursor = db.query(PrivateDataContract.TABLENAME, try (Cursor cursor = db.query(PrivateDataContract.TABLENAME, DATA_COLUMN, // columns PrivateDataContract.COLNAME_L2KEY + " = ? AND " // selection + PrivateDataContract.COLNAME_CLIENT + " = ? AND " Loading @@ -430,14 +427,13 @@ public class IpMemoryStoreDatabase { new String[] { key, clientId, name }, // selectionArgs null, // groupBy null, // having null); // orderBy // The query above is querying by (composite) primary key, so it should not be possible to // get more than one result here. 0 results means the key was not found. null)) { // orderBy // The query above is querying by (composite) primary key, so it should not be possible // to get more than one result here. 0 results means the key was not found. if (cursor.getCount() != 1) return null; cursor.moveToFirst(); final byte[] result = cursor.getBlob(0); // index in the DATA_COLUMN array cursor.close(); return result; return cursor.getBlob(0); // index in the DATA_COLUMN array } } /** Loading @@ -449,7 +445,7 @@ public class IpMemoryStoreDatabase { try { db.delete(NetworkAttributesContract.TABLENAME, null, null); db.delete(PrivateDataContract.TABLENAME, null, null); final Cursor cursorNetworkAttributes = db.query( try (Cursor cursorNetworkAttributes = db.query( // table name NetworkAttributesContract.TABLENAME, // column name Loading @@ -459,13 +455,10 @@ public class IpMemoryStoreDatabase { null, // groupBy null, // having null, // orderBy "1"); // limit if (0 != cursorNetworkAttributes.getCount()) { cursorNetworkAttributes.close(); continue; "1")) { // limit if (0 != cursorNetworkAttributes.getCount()) continue; } cursorNetworkAttributes.close(); final Cursor cursorPrivateData = db.query( try (Cursor cursorPrivateData = db.query( // table name PrivateDataContract.TABLENAME, // column name Loading @@ -475,14 +468,10 @@ public class IpMemoryStoreDatabase { null, // groupBy null, // having null, // orderBy "1"); // limit if (0 != cursorPrivateData.getCount()) { cursorPrivateData.close(); continue; "1")) { // limit if (0 != cursorPrivateData.getCount()) continue; } cursorPrivateData.close(); db.setTransactionSuccessful(); return; } catch (SQLiteException e) { Log.e(TAG, "Could not wipe the data in database", e); } finally { Loading Loading @@ -575,7 +564,7 @@ public class IpMemoryStoreDatabase { final String selection = NetworkAttributesContract.COLNAME_EXPIRYDATE + " > ? AND (" + sj.toString() + ")"; final Cursor cursor = db.queryWithFactory(new CustomCursorFactory(args), try (Cursor cursor = db.queryWithFactory(new CustomCursorFactory(args), false, // distinct NetworkAttributesContract.TABLENAME, null, // columns, null means everything Loading @@ -584,11 +573,12 @@ public class IpMemoryStoreDatabase { null, // groupBy null, // having null, // orderBy null); // limit null)) { // limit if (cursor.getCount() <= 0) return null; cursor.moveToFirst(); String bestKey = null; float bestMatchConfidence = GROUPCLOSE_CONFIDENCE; // Never return a match worse than this. float bestMatchConfidence = GROUPCLOSE_CONFIDENCE; // Never return a match worse than this. while (!cursor.isAfterLast()) { final NetworkAttributes read = readNetworkAttributesLine(cursor); final float confidence = read.getNetworkGroupSamenessConfidence(attr); Loading @@ -598,9 +588,9 @@ public class IpMemoryStoreDatabase { } cursor.moveToNext(); } cursor.close(); return bestKey; } } /** * Delete a single entry by key. Loading Loading @@ -695,20 +685,21 @@ public class IpMemoryStoreDatabase { } // Queries number of NetworkAttributes that start from the lowest expiryDate. final Cursor cursor = db.query(NetworkAttributesContract.TABLENAME, final long expiryDate; try (Cursor cursor = db.query(NetworkAttributesContract.TABLENAME, new String[] {NetworkAttributesContract.COLNAME_EXPIRYDATE}, // columns null, // selection null, // selectionArgs null, // groupBy null, // having NetworkAttributesContract.COLNAME_EXPIRYDATE, // orderBy Integer.toString(number)); // limit Integer.toString(number))) { // limit if (cursor == null || cursor.getCount() <= 0) return Status.ERROR_GENERIC; cursor.moveToLast(); // Get the expiryDate from last record. final long expiryDate = getLong(cursor, NetworkAttributesContract.COLNAME_EXPIRYDATE, 0); cursor.close(); expiryDate = getLong(cursor, NetworkAttributesContract.COLNAME_EXPIRYDATE, 0); } db.beginTransaction(); try { Loading Loading @@ -736,16 +727,17 @@ public class IpMemoryStoreDatabase { static int getTotalRecordNumber(@NonNull final SQLiteDatabase db) { // Query the total number of NetworkAttributes final Cursor cursor = db.query(NetworkAttributesContract.TABLENAME, try (Cursor cursor = db.query(NetworkAttributesContract.TABLENAME, new String[] {"COUNT(*)"}, // columns null, // selection null, // selectionArgs null, // groupBy null, // having null); // orderBy null)) { // orderBy cursor.moveToFirst(); return cursor == null ? 0 : cursor.getInt(0); } } // Helper methods private static String getString(final Cursor cursor, final String columnName) { Loading
tests/unit/src/com/android/networkstack/metrics/DataStallStatsUtilsTest.kt +73 −1 Original line number Diff line number Diff line Loading @@ -16,18 +16,33 @@ package com.android.networkstack.metrics import android.net.NetworkCapabilities.TRANSPORT_CELLULAR import android.net.captiveportal.CaptivePortalProbeResult import android.net.metrics.ValidationProbeEvent import android.net.util.DataStallUtils.DATA_STALL_EVALUATION_TYPE_DNS import android.telephony.TelephonyManager import androidx.test.filters.SmallTest import androidx.test.runner.AndroidJUnit4 import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession import com.android.dx.mockito.inline.extended.ExtendedMockito.verify import com.android.server.connectivity.nano.CellularData import com.android.server.connectivity.nano.DataStallEventProto import com.android.server.connectivity.nano.DnsEvent import com.google.protobuf.nano.MessageNano import java.util.Arrays import org.junit.Assert.assertEquals import org.junit.Test import org.junit.runner.RunWith import android.net.metrics.ValidationProbeEvent import org.mockito.ArgumentMatchers.eq @RunWith(AndroidJUnit4::class) @SmallTest class DataStallStatsUtilsTest { private val TEST_ELAPSED_TIME_MS = 123456789L private val TEST_MCCMNC = "123456" private val TEST_SIGNAL_STRENGTH = -100 private val RETURN_CODE_DNS_TIMEOUT = 255 @Test fun testProbeResultToEnum() { assertEquals(DataStallStatsUtils.probeResultToEnum(null), DataStallEventProto.INVALID) Loading @@ -53,4 +68,61 @@ class DataStallStatsUtilsTest { CaptivePortalProbeResult.PORTAL_CODE, ValidationProbeEvent.PROBE_HTTPS)), DataStallEventProto.PORTAL) } @Test fun testWrite() { val session = mockitoSession().spyStatic(NetworkStackStatsLog::class.java).startMocking() val stats = DataStallDetectionStats.Builder() .setEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS) .setNetworkType(TRANSPORT_CELLULAR) .setCellData(TelephonyManager.NETWORK_TYPE_LTE /* radioType */, true /* roaming */, TEST_MCCMNC /* networkMccmnc */, TEST_MCCMNC /* simMccmnc */, TEST_SIGNAL_STRENGTH /* signalStrength */) .setTcpFailRate(90) .setTcpSentSinceLastRecv(10) generateTimeoutDnsEvent(stats, count = 5) DataStallStatsUtils.write(stats.build(), CaptivePortalProbeResult.PARTIAL) verify { NetworkStackStatsLog.write( eq(NetworkStackStatsLog.DATA_STALL_EVENT), eq(DATA_STALL_EVALUATION_TYPE_DNS), eq(DataStallEventProto.PARTIAL), eq(TRANSPORT_CELLULAR), eq(DataStallDetectionStats.emptyWifiInfoIfNull(null)), eq(makeTestCellDataNano()), eq(makeTestDnsTimeoutNano(5)), eq(90) /* tcpFailRate */, eq(10) /* tcpSentSinceLastRecv */) } session.finishMocking() } private fun makeTestDnsTimeoutNano(timeoutCount: Int): ByteArray? { // Make an expected nano dns message. val event = DnsEvent() event.dnsReturnCode = IntArray(timeoutCount) event.dnsTime = LongArray(timeoutCount) Arrays.fill(event.dnsReturnCode, RETURN_CODE_DNS_TIMEOUT) Arrays.fill(event.dnsTime, TEST_ELAPSED_TIME_MS) return MessageNano.toByteArray(event) } private fun makeTestCellDataNano(): ByteArray? { // Make an expected nano cell data message. val data = CellularData() data.ratType = DataStallEventProto.RADIO_TECHNOLOGY_LTE data.networkMccmnc = TEST_MCCMNC data.simMccmnc = TEST_MCCMNC data.isRoaming = true data.signalStrength = TEST_SIGNAL_STRENGTH return MessageNano.toByteArray(data) } private fun generateTimeoutDnsEvent(stats: DataStallDetectionStats.Builder, count: Int) { repeat(count) { stats.addDnsEvent(RETURN_CODE_DNS_TIMEOUT, TEST_ELAPSED_TIME_MS) } } }