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

Commit 55ae8816 authored by Varun Shah's avatar Varun Shah
Browse files

Better error handling in UsageStats.

Ensure all types of exceptions are caught and logged/handled within
UsageStats.

Also update how read failures for pending events files are handled -
since the entire System DE directory is deleted on a successful user
unlock, read all the pending events that you can and skip over
potentially corrupted files.

Bug: 145574371
Test: atest UsageStatsDatabaseTest
Test: atest android.app.usage.cts.UsageStatsTest
Change-Id: I2e22fb01f9b97961b2160aeebaf7791d6673f8b9
parent a1248d31
Loading
Loading
Loading
Loading
+19 −17
Original line number Original line Diff line number Diff line
@@ -236,7 +236,7 @@ public class UsageStatsDatabase {
                        return false;
                        return false;
                    }
                    }
                }
                }
            } catch (IOException e) {
            } catch (Exception e) {
                Slog.e(TAG, "Failed to check-in", e);
                Slog.e(TAG, "Failed to check-in", e);
                return false;
                return false;
            }
            }
@@ -744,7 +744,7 @@ public class UsageStatsDatabase {
                IntervalStats stats = new IntervalStats();
                IntervalStats stats = new IntervalStats();
                readLocked(f, stats);
                readLocked(f, stats);
                return stats;
                return stats;
            } catch (IOException e) {
            } catch (Exception e) {
                Slog.e(TAG, "Failed to read usage stats file", e);
                Slog.e(TAG, "Failed to read usage stats file", e);
            }
            }
        }
        }
@@ -862,7 +862,7 @@ public class UsageStatsDatabase {
                    if (beginTime < stats.endTime) {
                    if (beginTime < stats.endTime) {
                        combiner.combine(stats, false, results);
                        combiner.combine(stats, false, results);
                    }
                    }
                } catch (IOException e) {
                } catch (Exception e) {
                    Slog.e(TAG, "Failed to read usage stats file", e);
                    Slog.e(TAG, "Failed to read usage stats file", e);
                    // We continue so that we return results that are not
                    // We continue so that we return results that are not
                    // corrupt.
                    // corrupt.
@@ -1009,7 +1009,8 @@ public class UsageStatsDatabase {
        }
        }
    }
    }


    private void writeLocked(AtomicFile file, IntervalStats stats) throws IOException {
    private void writeLocked(AtomicFile file, IntervalStats stats)
            throws IOException, RuntimeException {
        if (mCurrentVersion <= 3) {
        if (mCurrentVersion <= 3) {
            Slog.wtf(TAG, "Attempting to write UsageStats as XML with version " + mCurrentVersion);
            Slog.wtf(TAG, "Attempting to write UsageStats as XML with version " + mCurrentVersion);
            return;
            return;
@@ -1018,7 +1019,7 @@ public class UsageStatsDatabase {
    }
    }


    private static void writeLocked(AtomicFile file, IntervalStats stats, int version,
    private static void writeLocked(AtomicFile file, IntervalStats stats, int version,
            PackagesTokenData packagesTokenData) throws IOException {
            PackagesTokenData packagesTokenData) throws IOException, RuntimeException {
        FileOutputStream fos = file.startWrite();
        FileOutputStream fos = file.startWrite();
        try {
        try {
            writeLocked(fos, stats, version, packagesTokenData);
            writeLocked(fos, stats, version, packagesTokenData);
@@ -1031,7 +1032,7 @@ public class UsageStatsDatabase {
    }
    }


    private static void writeLocked(OutputStream out, IntervalStats stats, int version,
    private static void writeLocked(OutputStream out, IntervalStats stats, int version,
            PackagesTokenData packagesTokenData) throws IOException {
            PackagesTokenData packagesTokenData) throws RuntimeException {
        switch (version) {
        switch (version) {
            case 1:
            case 1:
            case 2:
            case 2:
@@ -1041,7 +1042,7 @@ public class UsageStatsDatabase {
            case 4:
            case 4:
                try {
                try {
                    UsageStatsProto.write(out, stats);
                    UsageStatsProto.write(out, stats);
                } catch (IOException | IllegalArgumentException e) {
                } catch (Exception e) {
                    Slog.e(TAG, "Unable to write interval stats to proto.", e);
                    Slog.e(TAG, "Unable to write interval stats to proto.", e);
                }
                }
                break;
                break;
@@ -1049,7 +1050,7 @@ public class UsageStatsDatabase {
                stats.obfuscateData(packagesTokenData);
                stats.obfuscateData(packagesTokenData);
                try {
                try {
                    UsageStatsProtoV2.write(out, stats);
                    UsageStatsProtoV2.write(out, stats);
                } catch (IOException | IllegalArgumentException e) {
                } catch (Exception e) {
                    Slog.e(TAG, "Unable to write interval stats to proto.", e);
                    Slog.e(TAG, "Unable to write interval stats to proto.", e);
                }
                }
                break;
                break;
@@ -1060,7 +1061,8 @@ public class UsageStatsDatabase {
        }
        }
    }
    }


    private void readLocked(AtomicFile file, IntervalStats statsOut) throws IOException {
    private void readLocked(AtomicFile file, IntervalStats statsOut)
            throws IOException, RuntimeException {
        if (mCurrentVersion <= 3) {
        if (mCurrentVersion <= 3) {
            Slog.wtf(TAG, "Reading UsageStats as XML; current database version: "
            Slog.wtf(TAG, "Reading UsageStats as XML; current database version: "
                    + mCurrentVersion);
                    + mCurrentVersion);
@@ -1072,7 +1074,7 @@ public class UsageStatsDatabase {
     * Returns {@code true} if any stats were omitted while reading, {@code false} otherwise.
     * Returns {@code true} if any stats were omitted while reading, {@code false} otherwise.
     */
     */
    private static boolean readLocked(AtomicFile file, IntervalStats statsOut, int version,
    private static boolean readLocked(AtomicFile file, IntervalStats statsOut, int version,
            PackagesTokenData packagesTokenData) throws IOException {
            PackagesTokenData packagesTokenData) throws IOException, RuntimeException {
        boolean dataOmitted = false;
        boolean dataOmitted = false;
        try {
        try {
            FileInputStream in = file.openRead();
            FileInputStream in = file.openRead();
@@ -1098,7 +1100,7 @@ public class UsageStatsDatabase {
     * Returns {@code true} if any stats were omitted while reading, {@code false} otherwise.
     * Returns {@code true} if any stats were omitted while reading, {@code false} otherwise.
     */
     */
    private static boolean readLocked(InputStream in, IntervalStats statsOut, int version,
    private static boolean readLocked(InputStream in, IntervalStats statsOut, int version,
            PackagesTokenData packagesTokenData) throws IOException {
            PackagesTokenData packagesTokenData) throws RuntimeException {
        boolean dataOmitted = false;
        boolean dataOmitted = false;
        switch (version) {
        switch (version) {
            case 1:
            case 1:
@@ -1114,14 +1116,14 @@ public class UsageStatsDatabase {
            case 4:
            case 4:
                try {
                try {
                    UsageStatsProto.read(in, statsOut);
                    UsageStatsProto.read(in, statsOut);
                } catch (IOException e) {
                } catch (Exception e) {
                    Slog.e(TAG, "Unable to read interval stats from proto.", e);
                    Slog.e(TAG, "Unable to read interval stats from proto.", e);
                }
                }
                break;
                break;
            case 5:
            case 5:
                try {
                try {
                    UsageStatsProtoV2.read(in, statsOut);
                    UsageStatsProtoV2.read(in, statsOut);
                } catch (IOException e) {
                } catch (Exception e) {
                    Slog.e(TAG, "Unable to read interval stats from proto.", e);
                    Slog.e(TAG, "Unable to read interval stats from proto.", e);
                }
                }
                dataOmitted = statsOut.deobfuscateData(packagesTokenData);
                dataOmitted = statsOut.deobfuscateData(packagesTokenData);
@@ -1145,7 +1147,7 @@ public class UsageStatsDatabase {


        try (FileInputStream in = new AtomicFile(mPackageMappingsFile).openRead()) {
        try (FileInputStream in = new AtomicFile(mPackageMappingsFile).openRead()) {
            UsageStatsProtoV2.readObfuscatedData(in, mPackagesTokenData);
            UsageStatsProtoV2.readObfuscatedData(in, mPackagesTokenData);
        } catch (IOException e) {
        } catch (Exception e) {
            Slog.e(TAG, "Failed to read the obfuscated packages mapping file.", e);
            Slog.e(TAG, "Failed to read the obfuscated packages mapping file.", e);
            return;
            return;
        }
        }
@@ -1174,7 +1176,7 @@ public class UsageStatsDatabase {
            UsageStatsProtoV2.writeObfuscatedData(fos, mPackagesTokenData);
            UsageStatsProtoV2.writeObfuscatedData(fos, mPackagesTokenData);
            file.finishWrite(fos);
            file.finishWrite(fos);
            fos = null;
            fos = null;
        } catch (IOException | IllegalArgumentException e) {
        } catch (Exception e) {
            Slog.e(TAG, "Unable to write obfuscated data to proto.", e);
            Slog.e(TAG, "Unable to write obfuscated data to proto.", e);
        } finally {
        } finally {
            file.failWrite(fos);
            file.failWrite(fos);
@@ -1414,8 +1416,8 @@ public class UsageStatsDatabase {
        try {
        try {
            stats.beginTime = in.readLong();
            stats.beginTime = in.readLong();
            readLocked(in, stats, version, mPackagesTokenData);
            readLocked(in, stats, version, mPackagesTokenData);
        } catch (IOException ioe) {
        } catch (Exception e) {
            Slog.d(TAG, "DeSerializing IntervalStats Failed", ioe);
            Slog.d(TAG, "DeSerializing IntervalStats Failed", e);
            stats = null;
            stats = null;
        }
        }
        return stats;
        return stats;
+10 −8
Original line number Original line Diff line number Diff line
@@ -653,18 +653,20 @@ public class UsageStatsService extends SystemService implements
        }
        }
        Arrays.sort(pendingEventsFiles);
        Arrays.sort(pendingEventsFiles);


        for (int i = 0; i < pendingEventsFiles.length; i++) {
        final int numFiles = pendingEventsFiles.length;
        for (int i = 0; i < numFiles; i++) {
            final AtomicFile af = new AtomicFile(pendingEventsFiles[i]);
            final AtomicFile af = new AtomicFile(pendingEventsFiles[i]);
            final LinkedList<Event> tmpEvents = new LinkedList<>();
            try {
            try {
                try (FileInputStream in = af.openRead()) {
                try (FileInputStream in = af.openRead()) {
                    UsageStatsProtoV2.readPendingEvents(in, pendingEvents);
                    UsageStatsProtoV2.readPendingEvents(in, tmpEvents);
                }
                }
            } catch (IOException e) {
                // only add to the pending events if the read was successful
                // Even if one file read fails, exit here to keep all events in order on disk -
                pendingEvents.addAll(tmpEvents);
                // they will be read and processed the next time user is unlocked.
            } catch (Exception e) {
                // Most likely trying to read a corrupted file - log the failure and continue
                // reading the other pending event files.
                Slog.e(TAG, "Could not read " + pendingEventsFiles[i] + " for user " + userId);
                Slog.e(TAG, "Could not read " + pendingEventsFiles[i] + " for user " + userId);
                pendingEvents.clear();
                return;
            }
            }
        }
        }
    }
    }
@@ -691,7 +693,7 @@ public class UsageStatsService extends SystemService implements
            af.finishWrite(fos);
            af.finishWrite(fos);
            fos = null;
            fos = null;
            pendingEvents.clear();
            pendingEvents.clear();
        } catch (IOException | IllegalArgumentException e) {
        } catch (Exception e) {
            Slog.e(TAG, "Failed to write " + pendingEventsFile.getAbsolutePath()
            Slog.e(TAG, "Failed to write " + pendingEventsFile.getAbsolutePath()
                    + " for user " + userId);
                    + " for user " + userId);
        } finally {
        } finally {