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

Commit 4b99fe8a authored by Yisroel Forta's avatar Yisroel Forta
Browse files

Handle failed AppStartInfo record write

The IOException this catches can leave the write in a bad state causing it to fail later and crash system server.
Store whether individual record writes succeeded and then fail the write if not.
Also manually delete record as it's unlikely to work any better on the next persist.

This was manually confirmed to be the problem by throwing IOException in AppStartInfo#writeToProto and confirming system server crashes.

Test: manually throw an IOException in AppStartInfo#writeToProto, ensure write fails and system server lives
Bug: 342319837
Flag: EXEMPT - bugfix
Change-Id: Id0fa47fdade53bd8f262fbdb8aa1f75136aadcbd
parent 5efe00ed
Loading
Loading
Loading
Loading
+29 −8
Original line number Diff line number Diff line
@@ -83,6 +83,7 @@ public final class AppStartInfoTracker {
    private static final int FOREACH_ACTION_NONE = 0;
    private static final int FOREACH_ACTION_REMOVE_ITEM = 1;
    private static final int FOREACH_ACTION_STOP_ITERATION = 2;
    private static final int FOREACH_ACTION_REMOVE_AND_STOP_ITERATION = 3;

    private static final String MONITORING_MODE_EMPTY_TEXT = "No records";

@@ -659,8 +660,13 @@ public final class AppStartInfoTracker {
        }
    }

    /**
     * Run provided callback for each packake in start info dataset.
     *
     * @return whether the for each completed naturally, false if it was stopped manually.
     */
    @GuardedBy("mLock")
    private void forEachPackageLocked(
    private boolean forEachPackageLocked(
            BiFunction<String, SparseArray<AppStartInfoContainer>, Integer> callback) {
        if (callback != null) {
            ArrayMap<String, SparseArray<AppStartInfoContainer>> map = mData.getMap();
@@ -670,14 +676,17 @@ public final class AppStartInfoTracker {
                        map.removeAt(i);
                        break;
                    case FOREACH_ACTION_STOP_ITERATION:
                        i = 0;
                        break;
                        return false;
                    case FOREACH_ACTION_REMOVE_AND_STOP_ITERATION:
                        map.removeAt(i);
                        return false;
                    case FOREACH_ACTION_NONE:
                    default:
                        break;
                }
            }
        }
        return true;
    }

    @GuardedBy("mLock")
@@ -870,13 +879,14 @@ public final class AppStartInfoTracker {
        }
        AtomicFile af = new AtomicFile(mProcStartInfoFile);
        FileOutputStream out = null;
        boolean succeeded;
        long now = System.currentTimeMillis();
        try {
            out = af.startWrite();
            ProtoOutputStream proto = new ProtoOutputStream(out);
            proto.write(AppsStartInfoProto.LAST_UPDATE_TIMESTAMP, now);
            synchronized (mLock) {
                forEachPackageLocked(
                succeeded = forEachPackageLocked(
                        (packageName, records) -> {
                            long token = proto.start(AppsStartInfoProto.PACKAGES);
                            proto.write(AppsStartInfoProto.Package.PACKAGE_NAME, packageName);
@@ -888,15 +898,26 @@ public final class AppStartInfoTracker {
                                } catch (IOException e) {
                                    Slog.w(TAG, "Unable to write app start info into persistent"
                                            + "storage: " + e);
                                    // There was likely an issue with this record that won't resolve
                                    // next time we try to persist so remove it. Also stop iteration
                                    // as we failed the write and need to start again from scratch.
                                    return AppStartInfoTracker
                                            .FOREACH_ACTION_REMOVE_AND_STOP_ITERATION;
                                }
                            }
                            proto.end(token);
                            return AppStartInfoTracker.FOREACH_ACTION_NONE;
                        });
                if (succeeded) {
                    mLastAppStartInfoPersistTimestamp = now;
                }
            }
            if (succeeded) {
                proto.flush();
                af.finishWrite(out);
            } else {
                af.failWrite(out);
            }
        } catch (IOException e) {
            Slog.w(TAG, "Unable to write historical app start info into persistent storage: " + e);
            af.failWrite(out);