Loading core/java/android/content/pm/DataLoaderManager.java +3 −2 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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(); } Loading core/java/android/content/pm/IDataLoaderManager.aidl +1 −1 Original line number Diff line number Diff line Loading @@ -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); Loading services/core/java/com/android/server/pm/DataLoaderManagerService.java +24 −12 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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(); } Loading @@ -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; Loading @@ -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); } /** Loading services/core/java/com/android/server/pm/PackageInstallerSession.java +3 −1 Original line number Diff line number Diff line Loading @@ -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"); } Loading services/incremental/IncrementalService.cpp +80 −4 Original line number Diff line number Diff line Loading @@ -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() { Loading Loading @@ -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; Loading @@ -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); } } Loading Loading @@ -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: Loading Loading @@ -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; Loading Loading @@ -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); } Loading Loading @@ -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 Loading
core/java/android/content/pm/DataLoaderManager.java +3 −2 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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(); } Loading
core/java/android/content/pm/IDataLoaderManager.aidl +1 −1 Original line number Diff line number Diff line Loading @@ -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); Loading
services/core/java/com/android/server/pm/DataLoaderManagerService.java +24 −12 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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(); } Loading @@ -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; Loading @@ -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); } /** Loading
services/core/java/com/android/server/pm/PackageInstallerSession.java +3 −1 Original line number Diff line number Diff line Loading @@ -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"); } Loading
services/incremental/IncrementalService.cpp +80 −4 Original line number Diff line number Diff line Loading @@ -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() { Loading Loading @@ -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; Loading @@ -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); } } Loading Loading @@ -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: Loading Loading @@ -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; Loading Loading @@ -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); } Loading Loading @@ -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