/* * This source is part of the * _____ ___ ____ * __ / / _ \/ _ | / __/___ _______ _ * / // / , _/ __ |/ _/_/ _ \/ __/ _ `/ * \___/_/|_/_/ |_/_/ (_)___/_/ \_, / * /___/ * repository. * * Copyright (C) 2013-2015 Carmen Alvarez (c@rmen.ca) * * 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 ca.rmen.android.networkmonitor.app.dbops.backend.export; import java.util.Map; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import java.util.TreeSet; import android.content.Context; import android.database.Cursor; import android.os.Build; import android.support.annotation.NonNull; import android.text.TextUtils; import android.text.format.DateUtils; import ca.rmen.android.networkmonitor.Constants; import ca.rmen.android.networkmonitor.Constants.ConnectionType; import ca.rmen.android.networkmonitor.R; import ca.rmen.android.networkmonitor.provider.ConnectionTestStatsColumns; import ca.rmen.android.networkmonitor.provider.NetMonColumns; import ca.rmen.android.networkmonitor.util.Log; public class SummaryExport { private static final String TAG = Constants.TAG + SummaryExport.class.getSimpleName(); /** * Contains data for network tests common to all network types. */ private static class TestResult implements Comparable<TestResult> { private int mPassCount, mFailCount, mSlowCount; private int mTestCount; int passRate; final String label; final String id1, id2, id3; private TestResult(String label, String id1, String id2, String id3) { if (TextUtils.isEmpty(label)) this.label = "*"; else this.label = label; this.id1 = id1; this.id2 = id2; this.id3 = id3; } private void calculatePassRate() { mTestCount = mPassCount + mFailCount + mSlowCount; passRate = mTestCount > 0 ? 100 * mPassCount / mTestCount : 0; } void setPassCount(int passCount) { mPassCount = passCount; calculatePassRate(); } void setFailCount(int failCount) { mFailCount = failCount; calculatePassRate(); } void setSlowCount(int slowCount) { mSlowCount = slowCount; calculatePassRate(); } @Override public String toString() { return passRate + "% (" + mTestCount + " tests)"; } private int compare(String s1, String s2) { if (s1 == null && s2 == null) return 0; if (s1 != null && s2 != null) return s1.compareTo(s2); if (s1 == null) return -1; return 1; } @Override public int compareTo(@NonNull TestResult other) { if (other.getClass().equals(other.getClass())) { // First, compare by score int rateDiff = passRate - other.passRate; if (rateDiff > 0) return 1; else if (rateDiff < 0) return -1; // If two TestResults have the same score, compare by their ids. final int id1compare = compare(id1, other.id1); if (id1compare != 0) return id1compare; final int id2compare = compare(id2, other.id2); if (id2compare != 0) return id2compare; final int id3compare = compare(id3, other.id3); if (id3compare != 0) return id3compare; // Same score and same ids, compare by number of tests. return mTestCount - other.mTestCount; } else { return getClass().getSimpleName().compareTo(other.getClass().getSimpleName()); } } } /** * Test data and GSM cell identifiers for a cell. */ private static class GsmCellResult extends TestResult { private GsmCellResult(String label, String lac, String longCellId, String shortCellId) { super(label, lac, longCellId, shortCellId); } @Override public String toString() { return "LAC=" + id1 + ",CID=" + id2 + "(" + id3 + "): " + super.toString(); } } /** * Test data and CDMA cell identifiers for a cell. */ private static class CdmaCellResult extends TestResult { private CdmaCellResult(String label, String baseStationId, String networkId, String systemId) { super(label, baseStationId, networkId, systemId); } @Override public String toString() { return "BSSID=" + id1 + ",SID=" + id2 + ",NID=" + id3 + ": " + super.toString(); } } /** * Test data for a WiFi access point. */ private static class WiFiResult extends TestResult { private WiFiResult(String ssid, String bssid) { super(ssid, bssid, null, null); } @Override public String toString() { return "BSSID=" + id1 + ": " + super.toString(); } } /** * @return a summary report listing the tested cells: the cell ids, % pass rate, and number of tests are shown. */ public static String getSummary(Context context) { Log.v(TAG, "getSummary"); String[] projection = new String[] { ConnectionTestStatsColumns.TYPE, ConnectionTestStatsColumns.ID1, ConnectionTestStatsColumns.ID2, ConnectionTestStatsColumns.ID3, ConnectionTestStatsColumns.LABEL, ConnectionTestStatsColumns.TEST_COUNT, ConnectionTestStatsColumns.TEST_RESULT }; String orderBy = ConnectionTestStatsColumns.ID1 + "," + ConnectionTestStatsColumns.ID2 + "," + ConnectionTestStatsColumns.ID3; Cursor c = context.getContentResolver().query(ConnectionTestStatsColumns.CONTENT_URI, projection, null, null, orderBy); SortedMap<String, TreeSet<TestResult>> testResults = new TreeMap<>(); if (c != null) { try { if (c.moveToFirst()) { TestResult lastTestResult = null; do { ConnectionType connectionType = ConnectionType.valueOf(c.getString(c.getColumnIndex(ConnectionTestStatsColumns.TYPE))); int resultCount = c.getInt(c.getColumnIndex(ConnectionTestStatsColumns.TEST_COUNT)); String resultType = c.getString(c.getColumnIndex(ConnectionTestStatsColumns.TEST_RESULT)); String label = c.getString(c.getColumnIndex(ConnectionTestStatsColumns.LABEL)); String id1 = c.getString(c.getColumnIndex(ConnectionTestStatsColumns.ID1)); String id2 = c.getString(c.getColumnIndex(ConnectionTestStatsColumns.ID2)); String id3 = c.getString(c.getColumnIndex(ConnectionTestStatsColumns.ID3)); if (lastTestResult == null) lastTestResult = createTestResult(connectionType, label, id1, id2, id3); boolean newCell = !TextUtils.equals(id1, lastTestResult.id1) || !TextUtils.equals(id2, lastTestResult.id2) || !TextUtils.equals(id3, lastTestResult.id3); if (newCell) { add(testResults, lastTestResult.label, lastTestResult); lastTestResult = createTestResult(connectionType, label, id1, id2, id3); } if (!TextUtils.isEmpty(resultType)) { switch(resultType) { case Constants.CONNECTION_TEST_PASS: lastTestResult.setPassCount(resultCount); break; case Constants.CONNECTION_TEST_FAIL: lastTestResult.setFailCount(resultCount); break; case Constants.CONNECTION_TEST_SLOW: lastTestResult.setSlowCount(resultCount); break; default: break; } } if (c.isLast()) add(testResults, lastTestResult.label, lastTestResult); } while (c.moveToNext()); } } finally { c.close(); } } return generateReport(context, testResults); } /** * @return a user-friendly string including the earliest and latest timestamps of all data collected. */ public static String getDataCollectionDateRange(Context context) { Log.v(TAG, "getDataCollectionDateRange"); String[] projection = new String[] { "MIN(" + NetMonColumns.TIMESTAMP + ")", "MAX(" + NetMonColumns.TIMESTAMP + ")" }; Cursor c = context.getContentResolver().query(NetMonColumns.CONTENT_URI, projection, null, null, null); String dateRange = ""; if (c != null) { if (c.moveToNext()) { long firstTimestamp = c.getLong(0); long lastTimestamp = c.getLong(1); dateRange = DateUtils.formatDateRange(context, firstTimestamp, lastTimestamp, DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_TIME); } c.close(); } return dateRange; } private static <T> void add(Map<String, TreeSet<T>> map, String key, T value) { TreeSet<T> set = map.get(key); if (set == null) { set = new TreeSet<>(); map.put(key, set); } set.add(value); } private static String generateReport(Context context, SortedMap<String, TreeSet<TestResult>> cellResults) { StringBuilder sb = new StringBuilder(); sb.append(Build.MODEL).append("/").append(Build.VERSION.RELEASE).append("\n"); if (cellResults == null || cellResults.size() == 0) { sb.append(context.getString(R.string.export_error_no_mobile_tests)); } else for (String extraInfo : cellResults.keySet()) { sb.append(extraInfo).append(":\n"); for (int i = 0; i < extraInfo.length(); i++) sb.append("-"); sb.append("\n"); Set<TestResult> cellResultsForExtraInfo = cellResults.get(extraInfo); for (TestResult cellResult : cellResultsForExtraInfo) sb.append(cellResult).append("\n"); sb.append("\n"); } return sb.toString(); } private static TestResult createTestResult(ConnectionType connectionType, String label, String id1, String id2, String id3) { switch (connectionType) { case GSM: return new GsmCellResult(label, id1, id2, id3); case CDMA: return new CdmaCellResult(label, id1, id2, id3); case WIFI: default: return new WiFiResult(label, id1); } } }