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

Commit eb63ed08 authored by Varun Shah's avatar Varun Shah Committed by Android (Google) Code Review
Browse files

Merge changes from topic "b143889121"

* changes:
  Prune obsolete UsageStats data on upgrade.
  Add a job to prune UsageStats data on package removals.
parents 70531992 5228cc20
Loading
Loading
Loading
Loading
+4 −0
Original line number Original line Diff line number Diff line
@@ -5045,6 +5045,10 @@
                 android:permission="android.permission.BIND_JOB_SERVICE" >
                 android:permission="android.permission.BIND_JOB_SERVICE" >
        </service>
        </service>


        <service android:name="com.android.server.usage.UsageStatsIdleService"
                 android:permission="android.permission.BIND_JOB_SERVICE" >
        </service>

        <service android:name="com.android.server.net.watchlist.ReportWatchlistJobService"
        <service android:name="com.android.server.net.watchlist.ReportWatchlistJobService"
                 android:permission="android.permission.BIND_JOB_SERVICE" >
                 android:permission="android.permission.BIND_JOB_SERVICE" >
        </service>
        </service>
+9 −0
Original line number Original line Diff line number Diff line
@@ -281,4 +281,13 @@ public abstract class UsageStatsManagerInternal {
            return mUsageRemaining;
            return mUsageRemaining;
        }
        }
    }
    }

    /**
     * Called by {@link com.android.server.usage.UsageStatsIdleService} when the device is idle to
     * prune usage stats data for uninstalled packages.
     *
     * @param userId the user associated with the job
     * @return {@code true} if the pruning was successful, {@code false} otherwise
     */
    public abstract boolean pruneUninstalledPackagesData(@UserIdInt int userId);
}
}
+101 −11
Original line number Original line Diff line number Diff line
@@ -45,6 +45,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.IOException;
import java.util.List;
import java.util.List;
import java.util.Locale;
import java.util.Locale;
import java.util.Set;


@RunWith(AndroidJUnit4.class)
@RunWith(AndroidJUnit4.class)
@SmallTest
@SmallTest
@@ -93,6 +94,8 @@ public class UsageStatsDatabaseTest {
                for (File f : usageFiles) {
                for (File f : usageFiles) {
                    f.delete();
                    f.delete();
                }
                }
            } else {
                intervalDir.delete();
            }
            }
        }
        }
    }
    }
@@ -587,6 +590,7 @@ public class UsageStatsDatabaseTest {
        db.readMappingsLocked();
        db.readMappingsLocked();
        db.init(1);
        db.init(1);
        db.putUsageStats(interval, mIntervalStats);
        db.putUsageStats(interval, mIntervalStats);
        db.writeMappingsLocked();


        final String removedPackage = "fake.package.name0";
        final String removedPackage = "fake.package.name0";
        // invoke handler call directly from test to remove package
        // invoke handler call directly from test to remove package
@@ -594,21 +598,21 @@ public class UsageStatsDatabaseTest {


        List<IntervalStats> stats = db.queryUsageStats(interval, 0, mEndTime,
        List<IntervalStats> stats = db.queryUsageStats(interval, 0, mEndTime,
                mIntervalStatsVerifier);
                mIntervalStatsVerifier);
        for (int i = 0; i < stats.size(); i++) {
        assertEquals(1, stats.size(),
            final IntervalStats stat = stats.get(i);
                "Only one interval stats object should exist for the given time range.");
        final IntervalStats stat = stats.get(0);
        if (stat.packageStats.containsKey(removedPackage)) {
        if (stat.packageStats.containsKey(removedPackage)) {
            fail("Found removed package " + removedPackage + " in package stats.");
            fail("Found removed package " + removedPackage + " in package stats.");
            return;
            return;
        }
        }
            for (int j = 0; j < stat.events.size(); j++) {
        for (int i = 0; i < stat.events.size(); i++) {
                final Event event = stat.events.get(j);
            final Event event = stat.events.get(i);
            if (removedPackage.equals(event.mPackage)) {
            if (removedPackage.equals(event.mPackage)) {
                fail("Found an event from removed package " + removedPackage);
                fail("Found an event from removed package " + removedPackage);
                return;
                return;
            }
            }
        }
        }
    }
    }
    }


    @Test
    @Test
    public void testPackageRetention() throws IOException {
    public void testPackageRetention() throws IOException {
@@ -617,4 +621,90 @@ public class UsageStatsDatabaseTest {
        verifyPackageNotRetained(UsageStatsManager.INTERVAL_MONTHLY);
        verifyPackageNotRetained(UsageStatsManager.INTERVAL_MONTHLY);
        verifyPackageNotRetained(UsageStatsManager.INTERVAL_YEARLY);
        verifyPackageNotRetained(UsageStatsManager.INTERVAL_YEARLY);
    }
    }

    private void verifyPackageDataIsRemoved(UsageStatsDatabase db, int interval,
            String removedPackage) {
        List<IntervalStats> stats = db.queryUsageStats(interval, 0, mEndTime,
                mIntervalStatsVerifier);
        assertEquals(1, stats.size(),
                "Only one interval stats object should exist for the given time range.");
        final IntervalStats stat = stats.get(0);
        if (stat.packageStats.containsKey(removedPackage)) {
            fail("Found removed package " + removedPackage + " in package stats.");
            return;
        }
        for (int i = 0; i < stat.events.size(); i++) {
            final Event event = stat.events.get(i);
            if (removedPackage.equals(event.mPackage)) {
                fail("Found an event from removed package " + removedPackage);
                return;
            }
        }
    }

    private void verifyPackageDataIsNotRemoved(UsageStatsDatabase db, int interval,
            Set<String> installedPackages) {
        List<IntervalStats> stats = db.queryUsageStats(interval, 0, mEndTime,
                mIntervalStatsVerifier);
        assertEquals(1, stats.size(),
                "Only one interval stats object should exist for the given time range.");
        final IntervalStats stat = stats.get(0);
        if (!stat.packageStats.containsAll(installedPackages)) {
            fail("Could not find some installed packages in package stats.");
            return;
        }
        // attempt to find an event from each installed package
        for (String installedPackage : installedPackages) {
            for (int i = 0; i < stat.events.size(); i++) {
                if (installedPackage.equals(stat.events.get(i).mPackage)) {
                    break;
                }
                if (i == stat.events.size() - 1) {
                    fail("Could not find any event for: " + installedPackage);
                    return;
                }
            }
        }
    }

    @Test
    public void testPackageDataIsRemoved() throws IOException {
        UsageStatsDatabase db = new UsageStatsDatabase(mTestDir);
        db.readMappingsLocked();
        db.init(1);

        // write stats to disk for each interval
        db.putUsageStats(UsageStatsManager.INTERVAL_DAILY, mIntervalStats);
        db.putUsageStats(UsageStatsManager.INTERVAL_WEEKLY, mIntervalStats);
        db.putUsageStats(UsageStatsManager.INTERVAL_MONTHLY, mIntervalStats);
        db.putUsageStats(UsageStatsManager.INTERVAL_YEARLY, mIntervalStats);
        db.writeMappingsLocked();

        final Set<String> installedPackages = mIntervalStats.packageStats.keySet();
        final String removedPackage = installedPackages.iterator().next();
        installedPackages.remove(removedPackage);

        // mimic a package uninstall
        db.onPackageRemoved(removedPackage, System.currentTimeMillis());

        // mimic the idle prune job being triggered
        db.pruneUninstalledPackagesData();

        // read data from disk into a new db instance
        UsageStatsDatabase newDB = new UsageStatsDatabase(mTestDir);
        newDB.readMappingsLocked();
        newDB.init(mEndTime);

        // query data for each interval and ensure data for package doesn't exist
        verifyPackageDataIsRemoved(newDB, UsageStatsManager.INTERVAL_DAILY, removedPackage);
        verifyPackageDataIsRemoved(newDB, UsageStatsManager.INTERVAL_WEEKLY, removedPackage);
        verifyPackageDataIsRemoved(newDB, UsageStatsManager.INTERVAL_MONTHLY, removedPackage);
        verifyPackageDataIsRemoved(newDB, UsageStatsManager.INTERVAL_YEARLY, removedPackage);

        // query data for each interval and ensure some data for installed packages exists
        verifyPackageDataIsNotRemoved(newDB, UsageStatsManager.INTERVAL_DAILY, installedPackages);
        verifyPackageDataIsNotRemoved(newDB, UsageStatsManager.INTERVAL_WEEKLY, installedPackages);
        verifyPackageDataIsNotRemoved(newDB, UsageStatsManager.INTERVAL_MONTHLY, installedPackages);
        verifyPackageDataIsNotRemoved(newDB, UsageStatsManager.INTERVAL_YEARLY, installedPackages);
    }
}
}
+19 −5
Original line number Original line Diff line number Diff line
@@ -448,8 +448,11 @@ public class IntervalStats {
    /**
    /**
     * Parses all of the tokens to strings in the obfuscated usage stats data. This includes
     * Parses all of the tokens to strings in the obfuscated usage stats data. This includes
     * deobfuscating each of the package tokens and chooser actions and categories.
     * deobfuscating each of the package tokens and chooser actions and categories.
     *
     * @return {@code true} if any stats were omitted while deobfuscating, {@code false} otherwise.
     */
     */
    private void deobfuscateUsageStats(PackagesTokenData packagesTokenData) {
    private boolean deobfuscateUsageStats(PackagesTokenData packagesTokenData) {
        boolean dataOmitted = false;
        final int usageStatsSize = packageStatsObfuscated.size();
        final int usageStatsSize = packageStatsObfuscated.size();
        for (int statsIndex = 0; statsIndex < usageStatsSize; statsIndex++) {
        for (int statsIndex = 0; statsIndex < usageStatsSize; statsIndex++) {
            final int packageToken = packageStatsObfuscated.keyAt(statsIndex);
            final int packageToken = packageStatsObfuscated.keyAt(statsIndex);
@@ -457,6 +460,7 @@ public class IntervalStats {
            usageStats.mPackageName = packagesTokenData.getPackageString(packageToken);
            usageStats.mPackageName = packagesTokenData.getPackageString(packageToken);
            if (usageStats.mPackageName == null) {
            if (usageStats.mPackageName == null) {
                Slog.e(TAG, "Unable to parse usage stats package " + packageToken);
                Slog.e(TAG, "Unable to parse usage stats package " + packageToken);
                dataOmitted = true;
                continue;
                continue;
            }
            }


@@ -489,14 +493,18 @@ public class IntervalStats {
            }
            }
            packageStats.put(usageStats.mPackageName, usageStats);
            packageStats.put(usageStats.mPackageName, usageStats);
        }
        }
        return dataOmitted;
    }
    }


    /**
    /**
     * Parses all of the tokens to strings in the obfuscated events data. This includes
     * Parses all of the tokens to strings in the obfuscated events data. This includes
     * deobfuscating the package token, along with any class, task root package/class tokens, and
     * deobfuscating the package token, along with any class, task root package/class tokens, and
     * shortcut or notification channel tokens.
     * shortcut or notification channel tokens.
     *
     * @return {@code true} if any events were omitted while deobfuscating, {@code false} otherwise.
     */
     */
    private void deobfuscateEvents(PackagesTokenData packagesTokenData) {
    private boolean deobfuscateEvents(PackagesTokenData packagesTokenData) {
        boolean dataOmitted = false;
        for (int i = this.events.size() - 1; i >= 0; i--) {
        for (int i = this.events.size() - 1; i >= 0; i--) {
            final Event event = this.events.get(i);
            final Event event = this.events.get(i);
            final int packageToken = event.mPackageToken;
            final int packageToken = event.mPackageToken;
@@ -504,6 +512,7 @@ public class IntervalStats {
            if (event.mPackage == null) {
            if (event.mPackage == null) {
                Slog.e(TAG, "Unable to parse event package " + packageToken);
                Slog.e(TAG, "Unable to parse event package " + packageToken);
                this.events.remove(i);
                this.events.remove(i);
                dataOmitted = true;
                continue;
                continue;
            }
            }


@@ -543,6 +552,7 @@ public class IntervalStats {
                        Slog.e(TAG, "Unable to parse shortcut " + event.mShortcutIdToken
                        Slog.e(TAG, "Unable to parse shortcut " + event.mShortcutIdToken
                                + " for package " + packageToken);
                                + " for package " + packageToken);
                        this.events.remove(i);
                        this.events.remove(i);
                        dataOmitted = true;
                        continue;
                        continue;
                    }
                    }
                    break;
                    break;
@@ -554,21 +564,25 @@ public class IntervalStats {
                                + event.mNotificationChannelIdToken + " for package "
                                + event.mNotificationChannelIdToken + " for package "
                                + packageToken);
                                + packageToken);
                        this.events.remove(i);
                        this.events.remove(i);
                        dataOmitted = true;
                        continue;
                        continue;
                    }
                    }
                    break;
                    break;
            }
            }
        }
        }
        return dataOmitted;
    }
    }


    /**
    /**
     * Parses the obfuscated tokenized data held in this interval stats object.
     * Parses the obfuscated tokenized data held in this interval stats object.
     *
     *
     * @return {@code true} if any data was omitted while deobfuscating, {@code false} otherwise.
     * @hide
     * @hide
     */
     */
    public void deobfuscateData(PackagesTokenData packagesTokenData) {
    public boolean deobfuscateData(PackagesTokenData packagesTokenData) {
        deobfuscateUsageStats(packagesTokenData);
        final boolean statsOmitted = deobfuscateUsageStats(packagesTokenData);
        deobfuscateEvents(packagesTokenData);
        final boolean eventsOmitted = deobfuscateEvents(packagesTokenData);
        return statsOmitted || eventsOmitted;
    }
    }


    /**
    /**
+5 −2
Original line number Original line Diff line number Diff line
@@ -162,15 +162,18 @@ public final class PackagesTokenData {
     *
     *
     * @param packageName the package to be removed
     * @param packageName the package to be removed
     * @param timeRemoved the time stamp of when the package was removed
     * @param timeRemoved the time stamp of when the package was removed
     * @return the token mapped to the package removed or {@code PackagesTokenData.UNASSIGNED_TOKEN}
     *         if not mapped
     */
     */
    public void removePackage(String packageName, long timeRemoved) {
    public int removePackage(String packageName, long timeRemoved) {
        removedPackagesMap.put(packageName, timeRemoved);
        removedPackagesMap.put(packageName, timeRemoved);


        if (!packagesToTokensMap.containsKey(packageName)) {
        if (!packagesToTokensMap.containsKey(packageName)) {
            return;
            return UNASSIGNED_TOKEN;
        }
        }
        final int packageToken = packagesToTokensMap.get(packageName).get(packageName);
        final int packageToken = packagesToTokensMap.get(packageName).get(packageName);
        packagesToTokensMap.remove(packageName);
        packagesToTokensMap.remove(packageName);
        tokensToPackagesMap.delete(packageToken);
        tokensToPackagesMap.delete(packageToken);
        return packageToken;
    }
    }
}
}
Loading