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

Commit 88916931 authored by Alex Buynytskyy's avatar Alex Buynytskyy
Browse files

Reduce locking during IO heavy operations.

Bug: 279339980
Test: atest ShortcutManagerTest1
Change-Id: I4ab3e9121232915dc2c98f9c4ad2a8feefb479c2
parent 9b957e80
Loading
Loading
Loading
Loading
+115 −98
Original line number Diff line number Diff line
@@ -93,7 +93,6 @@ import android.provider.DeviceConfig;
import android.text.TextUtils;
import android.text.format.TimeMigrationUtils;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.KeyValueListParser;
import android.util.Log;
import android.util.Slog;
@@ -149,6 +148,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.regex.Pattern;
@@ -321,8 +321,7 @@ public class ShortcutService extends IShortcutService.Stub {
    private final ArrayList<LauncherApps.ShortcutChangeCallback> mShortcutChangeCallbacks =
            new ArrayList<>(1);

    @GuardedBy("mLock")
    private long mRawLastResetTime;
    private final AtomicLong mRawLastResetTime = new AtomicLong(0);

    /**
     * User ID -> UserShortcuts
@@ -756,10 +755,15 @@ public class ShortcutService extends IShortcutService.Stub {
    }

    /** Return the base state file name */
    private AtomicFile getBaseStateFile() {
        final File path = new File(injectSystemDataPath(), FILENAME_BASE_STATE);
        path.mkdirs();
        return new AtomicFile(path);
    final ResilientAtomicFile getBaseStateFile() {
        File mainFile = new File(injectSystemDataPath(), FILENAME_BASE_STATE);
        File temporaryBackup = new File(injectSystemDataPath(),
                FILENAME_BASE_STATE + ".backup");
        File reserveCopy = new File(injectSystemDataPath(),
                FILENAME_BASE_STATE + ".reservecopy");
        int fileMode = FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IXOTH;
        return new ResilientAtomicFile(mainFile, temporaryBackup, reserveCopy, fileMode,
                "base shortcut", null);
    }

    /**
@@ -976,17 +980,18 @@ public class ShortcutService extends IShortcutService.Stub {
        writeAttr(out, name, intent.toUri(/* flags =*/ 0));
    }

    @GuardedBy("mLock")
    @VisibleForTesting
    void saveBaseStateLocked() {
        final AtomicFile file = getBaseStateFile();
    void saveBaseState() {
        try (ResilientAtomicFile file = getBaseStateFile()) {
            if (DEBUG || DEBUG_REBOOT) {
                Slog.d(TAG, "Saving to " + file.getBaseFile());
            }

            FileOutputStream outs = null;
            try {
                synchronized (mLock) {
                    outs = file.startWrite();
                }

                // Write to XML
                TypedXmlSerializer out = Xml.resolveSerializer(outs);
@@ -994,7 +999,8 @@ public class ShortcutService extends IShortcutService.Stub {
                out.startTag(null, TAG_ROOT);

                // Body.
            writeTagValue(out, TAG_LAST_RESET_TIME, mRawLastResetTime);
                // No locking required. Ok to add lock later if we save more data.
                writeTagValue(out, TAG_LAST_RESET_TIME, mRawLastResetTime.get());

                // Epilogue.
                out.endTag(null, TAG_ROOT);
@@ -1007,16 +1013,23 @@ public class ShortcutService extends IShortcutService.Stub {
                file.failWrite(outs);
            }
        }
    }

    @GuardedBy("mLock")
    private void loadBaseStateLocked() {
        mRawLastResetTime = 0;
        mRawLastResetTime.set(0);

        final AtomicFile file = getBaseStateFile();
        try (ResilientAtomicFile file = getBaseStateFile()) {
            if (DEBUG || DEBUG_REBOOT) {
                Slog.d(TAG, "Loading from " + file.getBaseFile());
            }
        try (FileInputStream in = file.openRead()) {
            FileInputStream in = null;
            try {
                in = file.openRead();
                if (in == null) {
                    throw new FileNotFoundException(file.getBaseFile().getAbsolutePath());
                }

                TypedXmlPullParser parser = Xml.resolvePullParser(in);

                int type;
@@ -1037,7 +1050,7 @@ public class ShortcutService extends IShortcutService.Stub {
                    // Assume depth == 2
                    switch (tag) {
                        case TAG_LAST_RESET_TIME:
                        mRawLastResetTime = parseLongAttribute(parser, ATTR_VALUE);
                            mRawLastResetTime.set(parseLongAttribute(parser, ATTR_VALUE));
                            break;
                        default:
                            Slog.e(TAG, "Invalid tag: " + tag);
@@ -1047,9 +1060,11 @@ public class ShortcutService extends IShortcutService.Stub {
            } catch (FileNotFoundException e) {
                // Use the default
            } catch (IOException | XmlPullParserException e) {
            Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e);

            mRawLastResetTime = 0;
                // Remove corrupted file and retry.
                file.failRead(in, e);
                loadBaseStateLocked();
                return;
            }
        }
        // Adjust the last reset time.
        getLastResetTimeLocked();
@@ -1067,8 +1082,7 @@ public class ShortcutService extends IShortcutService.Stub {
                "user shortcut", null);
    }

    @GuardedBy("mLock")
    private void saveUserLocked(@UserIdInt int userId) {
    private void saveUser(@UserIdInt int userId) {
        try (ResilientAtomicFile file = getUserFile(userId)) {
            FileOutputStream os = null;
            try {
@@ -1076,9 +1090,10 @@ public class ShortcutService extends IShortcutService.Stub {
                    Slog.d(TAG, "Saving to " + file);
                }

                synchronized (mLock) {
                    os = file.startWrite();

                    saveUserInternalLocked(userId, os, /* forBackup= */ false);
                }

                file.finishWrite(os);

@@ -1215,16 +1230,19 @@ public class ShortcutService extends IShortcutService.Stub {
        }
        try {
            Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "shortcutSaveDirtyInfo");
            List<Integer> dirtyUserIds = new ArrayList<>();
            synchronized (mLock) {
                for (int i = mDirtyUserIds.size() - 1; i >= 0; i--) {
                    final int userId = mDirtyUserIds.get(i);
                List<Integer> tmp = mDirtyUserIds;
                mDirtyUserIds = dirtyUserIds;
                dirtyUserIds = tmp;
            }
            for (int i = dirtyUserIds.size() - 1; i >= 0; i--) {
                final int userId = dirtyUserIds.get(i);
                if (userId == UserHandle.USER_NULL) { // USER_NULL for base state.
                        saveBaseStateLocked();
                    saveBaseState();
                } else {
                        saveUserLocked(userId);
                    }
                    saveUser(userId);
                }
                mDirtyUserIds.clear();
            }
        } catch (Exception e) {
            wtf("Exception in saveDirtyInfo", e);
@@ -1237,14 +1255,14 @@ public class ShortcutService extends IShortcutService.Stub {
    @GuardedBy("mLock")
    long getLastResetTimeLocked() {
        updateTimesLocked();
        return mRawLastResetTime;
        return mRawLastResetTime.get();
    }

    /** Return the next reset time. */
    @GuardedBy("mLock")
    long getNextResetTimeLocked() {
        updateTimesLocked();
        return mRawLastResetTime + mResetInterval;
        return mRawLastResetTime.get() + mResetInterval;
    }

    static boolean isClockValid(long time) {
@@ -1259,25 +1277,26 @@ public class ShortcutService extends IShortcutService.Stub {

        final long now = injectCurrentTimeMillis();

        final long prevLastResetTime = mRawLastResetTime;
        final long prevLastResetTime = mRawLastResetTime.get();
        long newLastResetTime = prevLastResetTime;

        if (mRawLastResetTime == 0) { // first launch.
        if (newLastResetTime == 0) { // first launch.
            // TODO Randomize??
            mRawLastResetTime = now;
        } else if (now < mRawLastResetTime) {
            newLastResetTime = now;
        } else if (now < newLastResetTime) {
            // Clock rewound.
            if (isClockValid(now)) {
                Slog.w(TAG, "Clock rewound");
                // TODO Randomize??
                mRawLastResetTime = now;
            }
        } else {
            if ((mRawLastResetTime + mResetInterval) <= now) {
                final long offset = mRawLastResetTime % mResetInterval;
                mRawLastResetTime = ((now / mResetInterval) * mResetInterval) + offset;
                newLastResetTime = now;
            }
        } else if ((newLastResetTime + mResetInterval) <= now) {
            final long offset = newLastResetTime % mResetInterval;
            newLastResetTime = ((now / mResetInterval) * mResetInterval) + offset;
        }
        if (prevLastResetTime != mRawLastResetTime) {

        mRawLastResetTime.set(newLastResetTime);
        if (prevLastResetTime != newLastResetTime) {
            scheduleSaveBaseState();
        }
    }
@@ -2705,9 +2724,7 @@ public class ShortcutService extends IShortcutService.Stub {
    }

    void resetAllThrottlingInner() {
        synchronized (mLock) {
            mRawLastResetTime = injectCurrentTimeMillis();
        }
        mRawLastResetTime.set(injectCurrentTimeMillis());
        scheduleSaveBaseState();
        Slog.i(TAG, "ShortcutManager: throttling counter reset for all users");
    }
@@ -2725,8 +2742,8 @@ public class ShortcutService extends IShortcutService.Stub {
            }
            getPackageShortcutsLocked(packageName, userId)
                    .resetRateLimitingForCommandLineNoSaving();
            saveUserLocked(userId);
        }
        saveUser(userId);
    }

    // We override this method in unit tests to do a simpler check.
@@ -4505,8 +4522,8 @@ public class ShortcutService extends IShortcutService.Stub {
                dumpCurrentTime(pw);
                pw.println();
            });
            saveUserLocked(userId);
        }
        saveUser(userId);
    }

    // === Dump ===
@@ -4717,9 +4734,9 @@ public class ShortcutService extends IShortcutService.Stub {
                pw.print(formatTime(now));

                pw.print("  Raw last reset: [");
                pw.print(mRawLastResetTime);
                pw.print(mRawLastResetTime.get());
                pw.print("] ");
                pw.print(formatTime(mRawLastResetTime));
                pw.print(formatTime(mRawLastResetTime.get()));

                final long last = getLastResetTimeLocked();
                pw.print("  Last reset: [");
+1 −1
Original line number Diff line number Diff line
@@ -201,7 +201,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
        mInjectedCurrentTimeMillis = START_TIME + 4 * INTERVAL + 50;
        assertResetTimes(START_TIME + 4 * INTERVAL, START_TIME + 5 * INTERVAL);

        mService.saveBaseStateLocked();
        mService.saveBaseState();

        dumpBaseStateFile();