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

Commit 9038560d authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Automerger Merge Worker
Browse files

Merge "DataLoader lifecycle." into sc-dev am: 8b8c4301

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/13509737

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: Ib72de1661a1d29a5798a708e2b64c67a3e658ca2
parents 6853f102 8b8c4301
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ public class DataLoaderManager {
     * @param dataLoaderId ID for the new data loader binder service.
     * @param params       DataLoaderParamsParcel object that contains data loader params, including
     *                     its package name, class name, and additional parameters.
     * @param bindDelayMs  introduce a delay before actual bind in case we want to avoid busylooping
     * @param listener     Callback for the data loader service to report status back to the
     *                     caller.
     * @return false if 1) target ID collides with a data loader that is already bound to data
@@ -48,9 +49,9 @@ public class DataLoaderManager {
     * or 4) fails to bind to the specified data loader service, otherwise return true.
     */
    public boolean bindToDataLoader(int dataLoaderId, @NonNull DataLoaderParamsParcel params,
            @NonNull IDataLoaderStatusListener listener) {
            long bindDelayMs, @NonNull IDataLoaderStatusListener listener) {
        try {
            return mService.bindToDataLoader(dataLoaderId, params, listener);
            return mService.bindToDataLoader(dataLoaderId, params, bindDelayMs, listener);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
+1 −1
Original line number Diff line number Diff line
@@ -23,7 +23,7 @@ import android.content.pm.IDataLoaderStatusListener;

/** @hide */
interface IDataLoaderManager {
    boolean bindToDataLoader(int id, in DataLoaderParamsParcel params,
    boolean bindToDataLoader(int id, in DataLoaderParamsParcel params, long bindDelayMs,
            IDataLoaderStatusListener listener);
    IDataLoader getDataLoader(int dataLoaderId);
    void unbindFromDataLoader(int dataLoaderId);
+24 −12
Original line number Diff line number Diff line
@@ -27,6 +27,8 @@ import android.content.pm.IDataLoaderManager;
import android.content.pm.IDataLoaderStatusListener;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -45,12 +47,20 @@ import java.util.List;
public class DataLoaderManagerService extends SystemService {
    private static final String TAG = "DataLoaderManager";
    private final Context mContext;
    private final HandlerThread mThread;
    private final Handler mHandler;
    private final DataLoaderManagerBinderService mBinderService;
    private SparseArray<DataLoaderServiceConnection> mServiceConnections = new SparseArray<>();

    public DataLoaderManagerService(Context context) {
        super(context);
        mContext = context;

        mThread = new HandlerThread(TAG);
        mThread.start();

        mHandler = new Handler(mThread.getLooper());

        mBinderService = new DataLoaderManagerBinderService();
    }

@@ -62,7 +72,7 @@ public class DataLoaderManagerService extends SystemService {
    final class DataLoaderManagerBinderService extends IDataLoaderManager.Stub {
        @Override
        public boolean bindToDataLoader(int dataLoaderId, DataLoaderParamsParcel params,
                IDataLoaderStatusListener listener) {
                long bindDelayMs, IDataLoaderStatusListener listener) {
            synchronized (mServiceConnections) {
                if (mServiceConnections.get(dataLoaderId) != null) {
                    return true;
@@ -76,19 +86,21 @@ public class DataLoaderManagerService extends SystemService {
            }

            // Binds to the specific data loader service.
            DataLoaderServiceConnection connection = new DataLoaderServiceConnection(dataLoaderId,
                    listener);
            final DataLoaderServiceConnection connection = new DataLoaderServiceConnection(
                    dataLoaderId, listener);

            Intent intent = new Intent();
            final Intent intent = new Intent();
            intent.setComponent(dataLoaderComponent);

            return mHandler.postDelayed(() -> {
                if (!mContext.bindServiceAsUser(intent, connection, Context.BIND_AUTO_CREATE,
                    UserHandle.of(UserHandle.getCallingUserId()))) {
                        mHandler, UserHandle.of(UserHandle.getCallingUserId()))) {
                    Slog.e(TAG,
                        "Failed to bind to: " + dataLoaderComponent + " for ID=" + dataLoaderId);
                            "Failed to bind to: " + dataLoaderComponent + " for ID="
                                    + dataLoaderId);
                    mContext.unbindService(connection);
                return false;
                }
            return true;
            }, bindDelayMs);
        }

        /**
+3 −1
Original line number Diff line number Diff line
@@ -3778,7 +3778,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
            }
        }

        if (!dataLoaderManager.bindToDataLoader(sessionId, params.getData(), statusListener)) {
        final long bindDelayMs = 0;
        if (!dataLoaderManager.bindToDataLoader(sessionId, params.getData(), bindDelayMs,
                statusListener)) {
            throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE,
                    "Failed to initialize data loader");
        }
+80 −4
Original line number Diff line number Diff line
@@ -69,6 +69,14 @@ struct Constants {
    static constexpr auto progressUpdateInterval = 1000ms;
    static constexpr auto perUidTimeoutOffset = progressUpdateInterval * 2;
    static constexpr auto minPerUidTimeout = progressUpdateInterval * 3;

    // If DL was up and not crashing for 10mins, we consider it healthy and reset all delays.
    static constexpr auto healthyDataLoaderUptime = 10min;
    // 10s, 100s (~2min), 1000s (~15min), 10000s (~3hrs)
    static constexpr auto minBindDelay = 10s;
    static constexpr auto maxBindDelay = 10000s;
    static constexpr auto bindDelayMultiplier = 10;
    static constexpr auto bindDelayJitterDivider = 10;
};

static const Constants& constants() {
@@ -386,6 +394,28 @@ void IncrementalService::onDump(int fd) {
    dprintf(fd, "}\n");
}

bool IncrementalService::needStartDataLoaderLocked(IncFsMount& ifs) {
    if (ifs.dataLoaderStub->params().packageName == Constants::systemPackage) {
        return true;
    }

    // Check all permanent binds.
    for (auto&& [_, bindPoint] : ifs.bindPoints) {
        if (bindPoint.kind != BindKind::Permanent) {
            continue;
        }
        const auto progress = getLoadingProgressFromPath(ifs, bindPoint.sourceDir,
                                                         /*stopOnFirstIncomplete=*/true);
        if (!progress.isError() && !progress.fullyLoaded()) {
            LOG(INFO) << "Non system mount: [" << bindPoint.sourceDir
                      << "], partial progress: " << progress.getProgress() * 100 << "%";
            return true;
        }
    }

    return false;
}

void IncrementalService::onSystemReady() {
    if (mSystemReady.exchange(true)) {
        return;
@@ -396,8 +426,11 @@ void IncrementalService::onSystemReady() {
        std::lock_guard l(mLock);
        mounts.reserve(mMounts.size());
        for (auto&& [id, ifs] : mMounts) {
            if (ifs->mountId == id &&
                ifs->dataLoaderStub->params().packageName == Constants::systemPackage) {
            if (ifs->mountId != id) {
                continue;
            }

            if (needStartDataLoaderLocked(*ifs)) {
                mounts.push_back(ifs);
            }
        }
@@ -1539,6 +1572,11 @@ static long elapsedMcs(Duration start, Duration end) {
    return std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
}

template <class Duration>
static constexpr auto castToMs(Duration d) {
    return std::chrono::duration_cast<std::chrono::milliseconds>(d);
}

// Extract lib files from zip, create new files in incfs and write data to them
// Lib files should be placed next to the APK file in the following matter:
// Example:
@@ -2134,9 +2172,43 @@ void IncrementalService::DataLoaderStub::setTargetStatusLocked(int status) {
               << status << " (current " << mCurrentStatus << ")";
}

Milliseconds IncrementalService::DataLoaderStub::updateBindDelay() {
    std::unique_lock lock(mMutex);
    const auto previousBindTs = mPreviousBindTs;
    const auto now = Clock::now();
    mPreviousBindTs = now;

    const auto nonCrashingInterval = std::max(castToMs(now - previousBindTs), 100ms);
    if (previousBindTs.time_since_epoch() == Clock::duration::zero() ||
        nonCrashingInterval > Constants::healthyDataLoaderUptime) {
        mPreviousBindDelay = 0ms;
        return mPreviousBindDelay;
    }

    constexpr auto minBindDelayMs = castToMs(Constants::minBindDelay);
    constexpr auto maxBindDelayMs = castToMs(Constants::maxBindDelay);

    const auto bindDelayMs =
            std::min(std::max(mPreviousBindDelay * Constants::bindDelayMultiplier, minBindDelayMs),
                     maxBindDelayMs)
                    .count();
    const auto bindDelayJitterRangeMs = bindDelayMs / Constants::bindDelayJitterDivider;
    const auto bindDelayJitterMs = rand() % (bindDelayJitterRangeMs * 2) - bindDelayJitterRangeMs;
    mPreviousBindDelay = std::chrono::milliseconds(bindDelayMs + bindDelayJitterMs);

    return mPreviousBindDelay;
}

bool IncrementalService::DataLoaderStub::bind() {
    const auto bindDelay = updateBindDelay();
    if (bindDelay > 1s) {
        LOG(INFO) << "Delaying bind to " << mParams.packageName << " by "
                  << bindDelay.count() / 1000 << "s";
    }

    bool result = false;
    auto status = mService.mDataLoaderManager->bindToDataLoader(id(), mParams, this, &result);
    auto status = mService.mDataLoaderManager->bindToDataLoader(id(), mParams, bindDelay.count(),
                                                                this, &result);
    if (!status.isOk() || !result) {
        LOG(ERROR) << "Failed to bind a data loader for mount " << id();
        return false;
@@ -2249,7 +2321,8 @@ binder::Status IncrementalService::DataLoaderStub::onStatusChanged(MountId mount

        listener = mStatusListener;

        if (mCurrentStatus == IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE) {
        if (mCurrentStatus == IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE ||
            mCurrentStatus == IDataLoaderStatusListener::DATA_LOADER_UNRECOVERABLE) {
            // For unavailable, unbind from DataLoader to ensure proper re-commit.
            setTargetStatusLocked(IDataLoaderStatusListener::DATA_LOADER_DESTROYED);
        }
@@ -2544,6 +2617,9 @@ void IncrementalService::DataLoaderStub::onDump(int fd) {
        dprintf(fd, "          blockIndex: %d\n", pendingRead.block);
        dprintf(fd, "          bootClockTsUs: %lld\n", (long long)pendingRead.bootClockTsUs);
    }
    dprintf(fd, "        bind: %llds ago (delay: %llds)\n",
            (long long)(elapsedMcs(mPreviousBindTs, Clock::now()) / 1000000),
            (long long)(mPreviousBindDelay.count() / 1000));
    dprintf(fd, "      }\n");
    const auto& params = mParams;
    dprintf(fd, "      dataLoaderParams: {\n");
Loading