Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 21c82c3e authored by Varun Shah's avatar Varun Shah
Browse files

Remove XML write functions from UsageStats.

UsageStats has moved away from XML and is now using protos. This removes
dead XML write functions which should ideally never be triggered. A
warning will be logged when reading XML data and a WTF when some
codepath attempts to write data in XML.

Certain tests are now marked as ignored since the XML write functions are
removed. It was decided to not move those functions into the test context
since the read/write code is so intertwined within the database logic.

Bug: 143187610
Test: atest UsageStatsDatabaseTest
Change-Id: If622b646e0edfe389e49aec9b51accdb9fbf1f45
parent 26d4e011
Loading
Loading
Loading
Loading
+0 −72
Original line number Diff line number Diff line
@@ -66,7 +66,6 @@ import com.android.internal.util.XmlUtils;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;

import java.io.IOException;
import java.lang.annotation.Retention;
@@ -2641,75 +2640,4 @@ public final class Configuration implements Parcelable, Comparable<Configuration
        // For persistence, we don't care about assetsSeq and WindowConfiguration, so do not read it
        // out.
    }


    /**
     * Writes the Configuration's member fields as attributes into the XmlSerializer.
     * The serializer is expected to have already started a tag so that attributes can be
     * immediately written.
     *
     * @param xml The serializer to which to write the attributes.
     * @param config The Configuration whose member fields to write.
     * {@hide}
     */
    public static void writeXmlAttrs(XmlSerializer xml, Configuration config) throws IOException {
        XmlUtils.writeIntAttribute(xml, XML_ATTR_FONT_SCALE,
                Float.floatToIntBits(config.fontScale));
        if (config.mcc != 0) {
            XmlUtils.writeIntAttribute(xml, XML_ATTR_MCC, config.mcc);
        }
        if (config.mnc != 0) {
            XmlUtils.writeIntAttribute(xml, XML_ATTR_MNC, config.mnc);
        }
        config.fixUpLocaleList();
        if (!config.mLocaleList.isEmpty()) {
           XmlUtils.writeStringAttribute(xml, XML_ATTR_LOCALES, config.mLocaleList.toLanguageTags());
        }
        if (config.touchscreen != TOUCHSCREEN_UNDEFINED) {
            XmlUtils.writeIntAttribute(xml, XML_ATTR_TOUCHSCREEN, config.touchscreen);
        }
        if (config.keyboard != KEYBOARD_UNDEFINED) {
            XmlUtils.writeIntAttribute(xml, XML_ATTR_KEYBOARD, config.keyboard);
        }
        if (config.keyboardHidden != KEYBOARDHIDDEN_UNDEFINED) {
            XmlUtils.writeIntAttribute(xml, XML_ATTR_KEYBOARD_HIDDEN, config.keyboardHidden);
        }
        if (config.hardKeyboardHidden != HARDKEYBOARDHIDDEN_UNDEFINED) {
            XmlUtils.writeIntAttribute(xml, XML_ATTR_HARD_KEYBOARD_HIDDEN,
                    config.hardKeyboardHidden);
        }
        if (config.navigation != NAVIGATION_UNDEFINED) {
            XmlUtils.writeIntAttribute(xml, XML_ATTR_NAVIGATION, config.navigation);
        }
        if (config.navigationHidden != NAVIGATIONHIDDEN_UNDEFINED) {
            XmlUtils.writeIntAttribute(xml, XML_ATTR_NAVIGATION_HIDDEN, config.navigationHidden);
        }
        if (config.orientation != ORIENTATION_UNDEFINED) {
            XmlUtils.writeIntAttribute(xml, XML_ATTR_ORIENTATION, config.orientation);
        }
        if (config.screenLayout != SCREENLAYOUT_UNDEFINED) {
            XmlUtils.writeIntAttribute(xml, XML_ATTR_SCREEN_LAYOUT, config.screenLayout);
        }
        if (config.colorMode != COLOR_MODE_UNDEFINED) {
            XmlUtils.writeIntAttribute(xml, XML_ATTR_COLOR_MODE, config.colorMode);
        }
        if (config.uiMode != 0) {
            XmlUtils.writeIntAttribute(xml, XML_ATTR_UI_MODE, config.uiMode);
        }
        if (config.screenWidthDp != SCREEN_WIDTH_DP_UNDEFINED) {
            XmlUtils.writeIntAttribute(xml, XML_ATTR_SCREEN_WIDTH, config.screenWidthDp);
        }
        if (config.screenHeightDp != SCREEN_HEIGHT_DP_UNDEFINED) {
            XmlUtils.writeIntAttribute(xml, XML_ATTR_SCREEN_HEIGHT, config.screenHeightDp);
        }
        if (config.smallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
            XmlUtils.writeIntAttribute(xml, XML_ATTR_SMALLEST_WIDTH, config.smallestScreenWidthDp);
        }
        if (config.densityDpi != DENSITY_DPI_UNDEFINED) {
            XmlUtils.writeIntAttribute(xml, XML_ATTR_DENSITY, config.densityDpi);
        }

        // For persistence, we do not care about assetsSeq and window configuration, so do not write
        // it out.
    }
}
+22 −9
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import static android.app.usage.UsageEvents.Event.MAX_EVENT_TYPE;
import static junit.framework.TestCase.fail;

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;

import android.app.usage.TimeSparseArray;
import android.app.usage.UsageEvents.Event;
@@ -35,6 +36,7 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;

import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;

@@ -423,6 +425,11 @@ public class UsageStatsDatabaseTest {
        prevDB.putUsageStats(UsageStatsManager.INTERVAL_DAILY, mIntervalStats);
        // Create a backup with a specific version
        byte[] blob = prevDB.getBackupPayload(KEY_USAGE_STATS, version);
        if (version >= 1 && version <= 3) {
            assertFalse(blob != null && blob.length != 0,
                    "UsageStatsDatabase shouldn't be able to write backups as XML");
            return;
        }

        clearUsageStatsFiles();

@@ -434,11 +441,9 @@ public class UsageStatsDatabaseTest {
        List<IntervalStats> stats = newDB.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, 0, mEndTime,
                mIntervalStatsVerifier);


        if (version > newDB.BACKUP_VERSION || version < 1) {
            if (stats != null && stats.size() != 0) {
                fail("UsageStatsDatabase should ne be able to restore from unknown data versions");
            }
        if (version > UsageStatsDatabase.BACKUP_VERSION || version < 1) {
            assertFalse(stats != null && !stats.isEmpty(),
                    "UsageStatsDatabase shouldn't be able to restore from unknown data versions");
            return;
        }

@@ -455,9 +460,12 @@ public class UsageStatsDatabaseTest {

    /**
     * Test the version upgrade from 3 to 4
     *
     * Ignored - version 3 is now deprecated.
     */
    @Ignore
    @Test
    public void testVersionUpgradeFrom3to4() throws IOException {
    public void ignore_testVersionUpgradeFrom3to4() throws IOException {
        runVersionChangeTest(3, 4, UsageStatsManager.INTERVAL_DAILY);
        runVersionChangeTest(3, 4, UsageStatsManager.INTERVAL_WEEKLY);
        runVersionChangeTest(3, 4, UsageStatsManager.INTERVAL_MONTHLY);
@@ -477,9 +485,12 @@ public class UsageStatsDatabaseTest {

    /**
     * Test the version upgrade from 3 to 5
     *
     * Ignored - version 3 is now deprecated.
     */
    @Ignore
    @Test
    public void testVersionUpgradeFrom3to5() throws IOException {
    public void ignore_testVersionUpgradeFrom3to5() throws IOException {
        runVersionChangeTest(3, 5, UsageStatsManager.INTERVAL_DAILY);
        runVersionChangeTest(3, 5, UsageStatsManager.INTERVAL_WEEKLY);
        runVersionChangeTest(3, 5, UsageStatsManager.INTERVAL_MONTHLY);
@@ -488,13 +499,15 @@ public class UsageStatsDatabaseTest {


    /**
     * Test the version upgrade from 3 to 4
     * Test backup/restore
     */
    @Test
    public void testBackupRestore() throws IOException {
        runBackupRestoreTest(1);
        runBackupRestoreTest(4);

        // test deprecated versions
        runBackupRestoreTest(1);

        // test invalid backup versions as well
        runBackupRestoreTest(0);
        runBackupRestoreTest(99999);
+28 −11
Original line number Diff line number Diff line
@@ -373,7 +373,12 @@ public class UsageStatsDatabase {
                Slog.e(TAG, "Failed read version upgrade breadcrumb");
                throw new RuntimeException(e);
            }
            if (mCurrentVersion >= 4) {
                continueUpgradeLocked(previousVersion, token);
            } else {
                Slog.wtf(TAG, "Attempting to upgrade to an unsupported version: "
                        + mCurrentVersion);
            }
        }

        if (version != mCurrentVersion || mNewUpdate) {
@@ -487,6 +492,9 @@ public class UsageStatsDatabase {
    }

    private void continueUpgradeLocked(int version, long token) {
        if (version <= 3) {
            Slog.w(TAG, "Reading UsageStats as XML; current database version: " + mCurrentVersion);
        }
        final File backupDir = new File(mBackupsDir, Long.toString(token));

        // Upgrade step logic for the entire usage stats directory, not individual interval dirs.
@@ -876,6 +884,10 @@ public class UsageStatsDatabase {
    }

    private void writeLocked(AtomicFile file, IntervalStats stats) throws IOException {
        if (mCurrentVersion <= 3) {
            Slog.wtf(TAG, "Attempting to write UsageStats as XML with version " + mCurrentVersion);
            return;
        }
        writeLocked(file, stats, mCurrentVersion, mPackagesTokenData);
    }

@@ -892,17 +904,13 @@ public class UsageStatsDatabase {
        }
    }

    private void writeLocked(OutputStream out, IntervalStats stats) throws IOException {
        writeLocked(out, stats, mCurrentVersion, mPackagesTokenData);
    }

    private static void writeLocked(OutputStream out, IntervalStats stats, int version,
            PackagesTokenData packagesTokenData) throws IOException {
        switch (version) {
            case 1:
            case 2:
            case 3:
                UsageStatsXml.write(out, stats);
                Slog.wtf(TAG, "Attempting to write UsageStats as XML with version " + version);
                break;
            case 4:
                try {
@@ -927,6 +935,10 @@ public class UsageStatsDatabase {
    }

    private void readLocked(AtomicFile file, IntervalStats statsOut) throws IOException {
        if (mCurrentVersion <= 3) {
            Slog.wtf(TAG, "Reading UsageStats as XML; current database version: "
                    + mCurrentVersion);
        }
        readLocked(file, statsOut, mCurrentVersion, mPackagesTokenData);
    }

@@ -951,17 +963,18 @@ public class UsageStatsDatabase {
        }
    }

    private void readLocked(InputStream in, IntervalStats statsOut) throws IOException {
        readLocked(in, statsOut, mCurrentVersion, mPackagesTokenData);
    }

    private static void readLocked(InputStream in, IntervalStats statsOut, int version,
            PackagesTokenData packagesTokenData) throws IOException {
        switch (version) {
            case 1:
            case 2:
            case 3:
                Slog.w(TAG, "Reading UsageStats as XML; database version: " + version);
                try {
                    UsageStatsXml.read(in, statsOut);
                } catch (Exception e) {
                    Slog.e(TAG, "Unable to read interval stats from XML", e);
                }
                break;
            case 4:
                try {
@@ -1076,6 +1089,10 @@ public class UsageStatsDatabase {
     */
    @VisibleForTesting
    public byte[] getBackupPayload(String key, int version) {
        if (version >= 1 && version <= 3) {
            Slog.wtf(TAG, "Attempting to backup UsageStats as XML with version " + version);
            return null;
        }
        synchronized (mLock) {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            if (KEY_USAGE_STATS.equals(key)) {
+1 −19
Original line number Diff line number Diff line
@@ -16,14 +16,11 @@

package com.android.server.usage;

import android.util.AtomicFile;
import android.util.Slog;
import android.util.Xml;
import android.util.proto.ProtoInputStream;
import android.util.proto.ProtoOutputStream;

import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

@@ -31,7 +28,6 @@ import java.io.*;

public class UsageStatsXml {
    private static final String TAG = "UsageStatsXml";
    private static final int CURRENT_VERSION = 1;
    private static final String USAGESTATS_TAG = "usagestats";
    private static final String VERSION_ATTR = "version";
    static final String CHECKED_IN_SUFFIX = "-c";
@@ -61,18 +57,4 @@ public class UsageStatsXml {
            throw new IOException(e);
        }
    }

    public static void write(OutputStream out, IntervalStats stats) throws IOException {
        FastXmlSerializer xml = new FastXmlSerializer();
        xml.setOutput(out, "utf-8");
        xml.startDocument("utf-8", true);
        xml.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
        xml.startTag(null, USAGESTATS_TAG);
        xml.attribute(null, VERSION_ATTR, Integer.toString(CURRENT_VERSION));

        UsageStatsXmlV1.write(xml, stats);

        xml.endTag(null, USAGESTATS_TAG);
        xml.endDocument();
    }
}
 No newline at end of file
+0 −175
Original line number Diff line number Diff line
@@ -26,7 +26,6 @@ import com.android.internal.util.XmlUtils;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;

import java.io.IOException;
import java.net.ProtocolException;
@@ -243,135 +242,6 @@ final class UsageStatsXmlV1 {
        statsOut.addEvent(event);
    }

    private static void writeUsageStats(XmlSerializer xml, final IntervalStats stats,
            final UsageStats usageStats) throws IOException {
        xml.startTag(null, PACKAGE_TAG);

        // Write the time offset.
        XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_ATTR,
                usageStats.mLastTimeUsed - stats.beginTime);
        XmlUtils.writeLongAttribute(xml, LAST_TIME_VISIBLE_ATTR,
                usageStats.mLastTimeVisible - stats.beginTime);
        XmlUtils.writeLongAttribute(xml, LAST_TIME_SERVICE_USED_ATTR,
                usageStats.mLastTimeForegroundServiceUsed - stats.beginTime);
        XmlUtils.writeStringAttribute(xml, PACKAGE_ATTR, usageStats.mPackageName);
        XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, usageStats.mTotalTimeInForeground);
        XmlUtils.writeLongAttribute(xml, TOTAL_TIME_VISIBLE_ATTR, usageStats.mTotalTimeVisible);
        XmlUtils.writeLongAttribute(xml, TOTAL_TIME_SERVICE_USED_ATTR,
                usageStats.mTotalTimeForegroundServiceUsed);
        XmlUtils.writeIntAttribute(xml, LAST_EVENT_ATTR, usageStats.mLastEvent);
        if (usageStats.mAppLaunchCount > 0) {
            XmlUtils.writeIntAttribute(xml, APP_LAUNCH_COUNT_ATTR, usageStats.mAppLaunchCount);
        }
        writeChooserCounts(xml, usageStats);
        xml.endTag(null, PACKAGE_TAG);
    }

    private static void writeCountAndTime(XmlSerializer xml, String tag, int count, long time)
            throws IOException {
        xml.startTag(null, tag);
        XmlUtils.writeIntAttribute(xml, COUNT_ATTR, count);
        XmlUtils.writeLongAttribute(xml, TIME_ATTR, time);
        xml.endTag(null, tag);
    }

    private static void writeChooserCounts(XmlSerializer xml, final UsageStats usageStats)
            throws IOException {
        if (usageStats == null || usageStats.mChooserCounts == null ||
                usageStats.mChooserCounts.keySet().isEmpty()) {
            return;
        }
        final int chooserCountSize = usageStats.mChooserCounts.size();
        for (int i = 0; i < chooserCountSize; i++) {
            final String action = usageStats.mChooserCounts.keyAt(i);
            final ArrayMap<String, Integer> counts = usageStats.mChooserCounts.valueAt(i);
            if (action == null || counts == null || counts.isEmpty()) {
                continue;
            }
            xml.startTag(null, CHOOSER_COUNT_TAG);
            XmlUtils.writeStringAttribute(xml, NAME, action);
            writeCountsForAction(xml, counts);
            xml.endTag(null, CHOOSER_COUNT_TAG);
        }
    }

    private static void writeCountsForAction(XmlSerializer xml, ArrayMap<String, Integer> counts)
            throws IOException {
        final int countsSize = counts.size();
        for (int i = 0; i < countsSize; i++) {
            String key = counts.keyAt(i);
            int count = counts.valueAt(i);
            if (count > 0) {
                xml.startTag(null, CATEGORY_TAG);
                XmlUtils.writeStringAttribute(xml, NAME, key);
                XmlUtils.writeIntAttribute(xml, COUNT, count);
                xml.endTag(null, CATEGORY_TAG);
            }
        }
    }

    private static void writeConfigStats(XmlSerializer xml, final IntervalStats stats,
            final ConfigurationStats configStats, boolean isActive) throws IOException {
        xml.startTag(null, CONFIG_TAG);

        // Write the time offset.
        XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_ATTR,
                configStats.mLastTimeActive - stats.beginTime);

        XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, configStats.mTotalTimeActive);
        XmlUtils.writeIntAttribute(xml, COUNT_ATTR, configStats.mActivationCount);
        if (isActive) {
            XmlUtils.writeBooleanAttribute(xml, ACTIVE_ATTR, true);
        }

        // Now write the attributes representing the configuration object.
        Configuration.writeXmlAttrs(xml, configStats.mConfiguration);

        xml.endTag(null, CONFIG_TAG);
    }

    private static void writeEvent(XmlSerializer xml, final IntervalStats stats,
            final UsageEvents.Event event) throws IOException {
        xml.startTag(null, EVENT_TAG);

        // Store the time offset.
        XmlUtils.writeLongAttribute(xml, TIME_ATTR, event.mTimeStamp - stats.beginTime);

        XmlUtils.writeStringAttribute(xml, PACKAGE_ATTR, event.mPackage);
        if (event.mClass != null) {
            XmlUtils.writeStringAttribute(xml, CLASS_ATTR, event.mClass);
        }
        XmlUtils.writeIntAttribute(xml, FLAGS_ATTR, event.mFlags);
        XmlUtils.writeIntAttribute(xml, TYPE_ATTR, event.mEventType);
        XmlUtils.writeIntAttribute(xml, INSTANCE_ID_ATTR, event.mInstanceId);

        switch (event.mEventType) {
            case UsageEvents.Event.CONFIGURATION_CHANGE:
                if (event.mConfiguration != null) {
                    Configuration.writeXmlAttrs(xml, event.mConfiguration);
                }
                break;
            case UsageEvents.Event.SHORTCUT_INVOCATION:
                if (event.mShortcutId != null) {
                    XmlUtils.writeStringAttribute(xml, SHORTCUT_ID_ATTR, event.mShortcutId);
                }
                break;
            case UsageEvents.Event.STANDBY_BUCKET_CHANGED:
                if (event.mBucketAndReason != 0) {
                    XmlUtils.writeIntAttribute(xml, STANDBY_BUCKET_ATTR, event.mBucketAndReason);
                }
                break;
            case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
                if (event.mNotificationChannelId != null) {
                    XmlUtils.writeStringAttribute(
                            xml, NOTIFICATION_CHANNEL_ATTR, event.mNotificationChannelId);
                }
                break;
        }

        xml.endTag(null, EVENT_TAG);
    }

    /**
     * Reads from the {@link XmlPullParser}, assuming that it is already on the
     * <code><usagestats></code> tag.
@@ -440,51 +310,6 @@ final class UsageStatsXmlV1 {
        }
    }

    /**
     * Writes the stats object to an XML file. The {@link XmlSerializer}
     * has already written the <code><usagestats></code> tag, but attributes may still
     * be added.
     *
     * @param xml The serializer to which to write the packageStats data.
     * @param stats The stats object to write to the XML file.
     */
    public static void write(XmlSerializer xml, IntervalStats stats) throws IOException {
        XmlUtils.writeLongAttribute(xml, END_TIME_ATTR, stats.endTime - stats.beginTime);
        XmlUtils.writeIntAttribute(xml, MAJOR_VERSION_ATTR, stats.majorVersion);
        XmlUtils.writeIntAttribute(xml, MINOR_VERSION_ATTR, stats.minorVersion);

        writeCountAndTime(xml, INTERACTIVE_TAG, stats.interactiveTracker.count,
                stats.interactiveTracker.duration);
        writeCountAndTime(xml, NON_INTERACTIVE_TAG, stats.nonInteractiveTracker.count,
                stats.nonInteractiveTracker.duration);
        writeCountAndTime(xml, KEYGUARD_SHOWN_TAG, stats.keyguardShownTracker.count,
                stats.keyguardShownTracker.duration);
        writeCountAndTime(xml, KEYGUARD_HIDDEN_TAG, stats.keyguardHiddenTracker.count,
                stats.keyguardHiddenTracker.duration);

        xml.startTag(null, PACKAGES_TAG);
        final int statsCount = stats.packageStats.size();
        for (int i = 0; i < statsCount; i++) {
            writeUsageStats(xml, stats, stats.packageStats.valueAt(i));
        }
        xml.endTag(null, PACKAGES_TAG);

        xml.startTag(null, CONFIGURATIONS_TAG);
        final int configCount = stats.configurations.size();
        for (int i = 0; i < configCount; i++) {
            boolean active = stats.activeConfiguration.equals(stats.configurations.keyAt(i));
            writeConfigStats(xml, stats, stats.configurations.valueAt(i), active);
        }
        xml.endTag(null, CONFIGURATIONS_TAG);

        xml.startTag(null, EVENT_LOG_TAG);
        final int eventCount = stats.events.size();
        for (int i = 0; i < eventCount; i++) {
            writeEvent(xml, stats, stats.events.get(i));
        }
        xml.endTag(null, EVENT_LOG_TAG);
    }

    private UsageStatsXmlV1() {
    }
}