[incremental/pm] register progress listener

Incremental Serivce periodically polls loading progress and sends to
Package Manager Service. Package Manager provides APIs for other
interested parties to listen to the loading progress.

BUG: 165841827
Test: unit test
Change-Id: I44b9e17c2240b9efe53bc09fc728b6671f1f7dfe
This commit is contained in:
Songchun Fan
2020-09-03 11:45:53 -07:00
parent dc8a48f161
commit a70985945b
16 changed files with 513 additions and 70 deletions

View File

@@ -1029,6 +1029,7 @@ filegroup {
name: "incremental_manager_aidl",
srcs: [
"core/java/android/os/incremental/IIncrementalService.aidl",
"core/java/android/os/incremental/IStorageLoadingProgressListener.aidl",
"core/java/android/os/incremental/IncrementalNewFileParams.aidl",
"core/java/android/os/incremental/IStorageHealthListener.aidl",
"core/java/android/os/incremental/StorageHealthCheckParams.aidl",

View File

@@ -0,0 +1,25 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.content.pm;
/**
* Callbacks for Package Manager to report package loading progress to listeners.
* @hide
*/
oneway interface IPackageLoadingProgressCallback {
void onPackageLoadingProgressChanged(float progress);
}

View File

@@ -31,6 +31,7 @@ import android.content.pm.IPackageInstaller;
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.IPackageDeleteObserver2;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageLoadingProgressCallback;
import android.content.pm.IPackageMoveObserver;
import android.content.pm.IPackageStatsObserver;
import android.content.pm.IntentFilterVerificationInfo;

View File

@@ -19,6 +19,7 @@ package android.os.incremental;
import android.content.pm.DataLoaderParamsParcel;
import android.content.pm.IDataLoaderStatusListener;
import android.os.incremental.IncrementalNewFileParams;
import android.os.incremental.IStorageLoadingProgressListener;
import android.os.incremental.IStorageHealthListener;
import android.os.incremental.StorageHealthCheckParams;
@@ -133,4 +134,14 @@ interface IIncrementalService {
* Waits until all native library extraction is done for the storage
*/
boolean waitForNativeBinariesExtraction(int storageId);
/**
* Register to start listening for loading progress change for a storage.
*/
boolean registerLoadingProgressListener(int storageId, IStorageLoadingProgressListener listener);
/**
* Stop listening for the loading progress change for a storage.
*/
boolean unregisterLoadingProgressListener(int storageId);
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.os.incremental;
/**
* Callbacks for Incremental Service to report storage loading progress to Package Manager Service.
* @hide
*/
oneway interface IStorageLoadingProgressListener {
void onStorageLoadingProgressChanged(int storageId, float progress);
}

View File

@@ -23,6 +23,8 @@ import android.annotation.SystemService;
import android.content.Context;
import android.content.pm.DataLoaderParams;
import android.content.pm.IDataLoaderStatusListener;
import android.content.pm.IPackageLoadingProgressCallback;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.SparseArray;
@@ -76,27 +78,14 @@ public final class IncrementalManager {
}
private final @Nullable IIncrementalService mService;
@GuardedBy("mStorages")
private final SparseArray<IncrementalStorage> mStorages = new SparseArray<>();
private final LoadingProgressCallbacks mLoadingProgressCallbacks =
new LoadingProgressCallbacks();
public IncrementalManager(IIncrementalService service) {
mService = service;
}
/**
* Returns a storage object given a storage ID.
*
* @param storageId The storage ID to identify the storage object.
* @return IncrementalStorage object corresponding to storage ID.
*/
// TODO(b/136132412): remove this
@Nullable
public IncrementalStorage getStorage(int storageId) {
synchronized (mStorages) {
return mStorages.get(storageId);
}
}
/**
* Opens or create an Incremental File System mounted directory and returns an
* IncrementalStorage object.
@@ -123,9 +112,6 @@ public final class IncrementalManager {
return null;
}
final IncrementalStorage storage = new IncrementalStorage(mService, id);
synchronized (mStorages) {
mStorages.put(id, storage);
}
if (autoStartDataLoader) {
storage.startLoading();
}
@@ -150,9 +136,6 @@ public final class IncrementalManager {
return null;
}
final IncrementalStorage storage = new IncrementalStorage(mService, id);
synchronized (mStorages) {
mStorages.put(id, storage);
}
return storage;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -173,11 +156,7 @@ public final class IncrementalManager {
if (id < 0) {
return null;
}
final IncrementalStorage storage = new IncrementalStorage(mService, id);
synchronized (mStorages) {
mStorages.put(id, storage);
}
return storage;
return new IncrementalStorage(mService, id);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -262,25 +241,6 @@ public final class IncrementalManager {
});
}
/**
* Closes a storage specified by the absolute path. If the path is not Incremental, do nothing.
* Unbinds the target dir and deletes the corresponding storage instance.
*/
public void closeStorage(@NonNull String path) {
try {
final int id = mService.openStorage(path);
if (id < 0) {
return;
}
mService.deleteStorage(id);
synchronized (mStorages) {
mStorages.remove(id);
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Checks if Incremental feature is enabled on this device.
*/
@@ -311,6 +271,149 @@ public final class IncrementalManager {
return nativeUnsafeGetFileSignature(path);
}
/**
* Closes a storage specified by the absolute path. If the path is not Incremental, do nothing.
* Unbinds the target dir and deletes the corresponding storage instance.
* Deletes the package name and associated storage id from maps.
*/
public void onPackageRemoved(@NonNull String codePath) {
try {
final IncrementalStorage storage = openStorage(codePath);
if (storage == null) {
return;
}
mLoadingProgressCallbacks.cleanUpCallbacks(storage);
mService.deleteStorage(storage.getId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Called when a new callback wants to listen to the loading progress of an installed package.
* Increment the count of callbacks associated to the corresponding storage.
* Only register storage listener if there hasn't been any existing callback on the storage yet.
* @param codePath Path of the installed package. This path is on an Incremental Storage.
* @param callback To report loading progress to.
* @return True if the package name and associated storage id are valid. False otherwise.
*/
public boolean registerCallback(@NonNull String codePath,
@NonNull IPackageLoadingProgressCallback callback) {
final IncrementalStorage storage = openStorage(codePath);
if (storage == null) {
// storage does not exist, package not installed
return false;
}
return mLoadingProgressCallbacks.registerCallback(storage, callback);
}
/**
* Called when a callback wants to stop listen to the loading progress of an installed package.
* Decrease the count of the callbacks on the associated to the corresponding storage.
* If the count becomes zero, unregister the storage listener.
* @param codePath Path of the installed package
* @return True if the package name and associated storage id are valid. False otherwise.
*/
public boolean unregisterCallback(@NonNull String codePath,
@NonNull IPackageLoadingProgressCallback callback) {
final IncrementalStorage storage = openStorage(codePath);
if (storage == null) {
// storage does not exist, package not installed
return false;
}
return mLoadingProgressCallbacks.unregisterCallback(storage, callback);
}
private static class LoadingProgressCallbacks extends IStorageLoadingProgressListener.Stub {
@GuardedBy("mCallbacks")
private final SparseArray<RemoteCallbackList<IPackageLoadingProgressCallback>> mCallbacks =
new SparseArray<>();
// TODO(b/165841827): disable callbacks when app state changes to fully loaded
public void cleanUpCallbacks(@NonNull IncrementalStorage storage) {
final int storageId = storage.getId();
final RemoteCallbackList<IPackageLoadingProgressCallback> callbacksForStorage;
synchronized (mCallbacks) {
callbacksForStorage = mCallbacks.removeReturnOld(storageId);
}
if (callbacksForStorage == null) {
return;
}
// Unregister all existing callbacks on this storage
callbacksForStorage.kill();
storage.unregisterLoadingProgressListener();
}
// TODO(b/165841827): handle reboot and app update
public boolean registerCallback(@NonNull IncrementalStorage storage,
@NonNull IPackageLoadingProgressCallback callback) {
final int storageId = storage.getId();
synchronized (mCallbacks) {
RemoteCallbackList<IPackageLoadingProgressCallback> callbacksForStorage =
mCallbacks.get(storageId);
if (callbacksForStorage == null) {
callbacksForStorage = new RemoteCallbackList<>();
mCallbacks.put(storageId, callbacksForStorage);
}
// Registration in RemoteCallbackList needs to be done first, such that when events
// come from Incremental Service, the callback is already registered
callbacksForStorage.register(callback);
if (callbacksForStorage.getRegisteredCallbackCount() > 1) {
// already listening for progress for this storage
return true;
}
}
return storage.registerLoadingProgressListener(this);
}
public boolean unregisterCallback(@NonNull IncrementalStorage storage,
@NonNull IPackageLoadingProgressCallback callback) {
final int storageId = storage.getId();
final RemoteCallbackList<IPackageLoadingProgressCallback> callbacksForStorage;
synchronized (mCallbacks) {
callbacksForStorage = mCallbacks.get(storageId);
if (callbacksForStorage == null) {
// no callback has ever been registered on this storage
return false;
}
if (!callbacksForStorage.unregister(callback)) {
// the callback was not registered
return false;
}
if (callbacksForStorage.getRegisteredCallbackCount() > 0) {
// other callbacks are still listening on this storage
return true;
}
mCallbacks.delete(storageId);
}
// stop listening for this storage
return storage.unregisterLoadingProgressListener();
}
@Override
public void onStorageLoadingProgressChanged(int storageId, float progress) {
final RemoteCallbackList<IPackageLoadingProgressCallback> callbacksForStorage;
synchronized (mCallbacks) {
callbacksForStorage = mCallbacks.get(storageId);
}
if (callbacksForStorage == null) {
// no callback has ever been registered on this storage
return;
}
final int n = callbacksForStorage.beginBroadcast();
// RemoteCallbackList use ArrayMap internally and it's safe to iterate this way
for (int i = 0; i < n; i++) {
final IPackageLoadingProgressCallback callback =
callbacksForStorage.getBroadcastItem(i);
try {
callback.onPackageLoadingProgressChanged(progress);
} catch (RemoteException ignored) {
}
}
callbacksForStorage.finishBroadcast();
}
}
/* Native methods */
private static native boolean nativeIsEnabled();
private static native boolean nativeIsIncrementalPath(@NonNull String path);

View File

@@ -519,4 +519,29 @@ public final class IncrementalStorage {
return false;
}
}
/**
* Register to listen to loading progress of all the files on this storage.
* @param listener To report progress from Incremental Service to the caller.
*/
public boolean registerLoadingProgressListener(IStorageLoadingProgressListener listener) {
try {
return mService.registerLoadingProgressListener(mId, listener);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
return false;
}
}
/**
* Unregister to stop listening to storage loading progress.
*/
public boolean unregisterLoadingProgressListener() {
try {
return mService.unregisterLoadingProgressListener(mId);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
return false;
}
}
}

View File

@@ -32,11 +32,16 @@ import android.content.pm.PackageManager.PackageInfoFlags;
import android.content.pm.PackageManager.ResolveInfoFlags;
import android.content.pm.parsing.component.ParsedMainComponent;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.Looper;
import android.os.PersistableBundle;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.SparseArray;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.pm.PackageList;
import com.android.server.pm.PackageSetting;
import com.android.server.pm.parsing.pkg.AndroidPackage;
@@ -46,6 +51,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
/**
@@ -986,4 +992,72 @@ public abstract class PackageManagerInternal {
* Returns {@code true} if the package is suspending any packages for the user.
*/
public abstract boolean isSuspendingAnyPackages(String suspendingPackage, int userId);
/**
* Register to listen for loading progress of an installed package.
* @param packageName The name of the installed package
* @param callback To loading reporting progress
* @param userId The user under which to check.
* @return Whether the registration was successful. It can fail if the package has not been
* installed yet.
*/
public abstract boolean registerInstalledLoadingProgressCallback(@NonNull String packageName,
@NonNull InstalledLoadingProgressCallback callback, int userId);
/**
* Unregister to stop listening to loading progress of an installed package
* @param packageName The name of the installed package
* @param callback To unregister
* @return True if the callback is removed from registered callback list. False is the callback
* does not exist on the registered callback list, which can happen if the callback has
* already been unregistered.
*/
public abstract boolean unregisterInstalledLoadingProgressCallback(@NonNull String packageName,
@NonNull InstalledLoadingProgressCallback callback);
/**
* Callback to listen for loading progress of a package installed on Incremental File System.
*/
public abstract static class InstalledLoadingProgressCallback {
final LoadingProgressCallbackBinder mBinder = new LoadingProgressCallbackBinder();
final Executor mExecutor;
/**
* Default constructor that should always be called on subclass instantiation
* @param handler To dispatch callback events through. If null, the main thread
* handler will be used.
*/
public InstalledLoadingProgressCallback(@Nullable Handler handler) {
if (handler == null) {
handler = new Handler(Looper.getMainLooper());
}
mExecutor = new HandlerExecutor(handler);
}
/**
* Binder used by Package Manager Service to register as a callback
* @return the binder object of IPackageLoadingProgressCallback
*/
public final @NonNull IBinder getBinder() {
return mBinder;
}
/**
* Report loading progress of an installed package.
*
* @param progress Loading progress between [0, 1] for the registered package.
*/
public abstract void onLoadingProgressChanged(float progress);
private class LoadingProgressCallbackBinder extends
android.content.pm.IPackageLoadingProgressCallback.Stub {
@Override
public void onPackageLoadingProgressChanged(float progress) {
mExecutor.execute(PooledLambda.obtainRunnable(
InstalledLoadingProgressCallback::onLoadingProgressChanged,
InstalledLoadingProgressCallback.this,
progress).recycleOnUse());
}
}
}
}

View File

@@ -176,6 +176,7 @@ import android.content.pm.IPackageDeleteObserver;
import android.content.pm.IPackageDeleteObserver2;
import android.content.pm.IPackageInstallObserver2;
import android.content.pm.IPackageInstaller;
import android.content.pm.IPackageLoadingProgressCallback;
import android.content.pm.IPackageManager;
import android.content.pm.IPackageManagerNative;
import android.content.pm.IPackageMoveObserver;
@@ -15993,7 +15994,7 @@ public class PackageManagerService extends IPackageManager.Stub
String codePath = codeFile.getAbsolutePath();
if (mIncrementalManager != null && isIncrementalPath(codePath)) {
mIncrementalManager.closeStorage(codePath);
mIncrementalManager.onPackageRemoved(codePath);
}
removeCodePathLI(codeFile);
@@ -17125,10 +17126,11 @@ public class PackageManagerService extends IPackageManager.Stub
& PackageManagerService.SCAN_AS_INSTANT_APP) != 0);
final AndroidPackage pkg = reconciledPkg.pkgSetting.pkg;
final String packageName = pkg.getPackageName();
final String codePath = pkg.getPath();
final boolean onIncremental = mIncrementalManager != null
&& isIncrementalPath(pkg.getPath());
&& isIncrementalPath(codePath);
if (onIncremental) {
IncrementalStorage storage = mIncrementalManager.openStorage(pkg.getPath());
IncrementalStorage storage = mIncrementalManager.openStorage(codePath);
if (storage == null) {
throw new IllegalArgumentException(
"Install: null storage for incremental package " + packageName);
@@ -25359,6 +25361,56 @@ public class PackageManagerService extends IPackageManager.Stub
public boolean isSuspendingAnyPackages(String suspendingPackage, int userId) {
return PackageManagerService.this.isSuspendingAnyPackages(suspendingPackage, userId);
}
@Override
public boolean registerInstalledLoadingProgressCallback(String packageName,
PackageManagerInternal.InstalledLoadingProgressCallback callback, int userId) {
final int callingUid = Binder.getCallingUid();
mPermissionManager.enforceCrossUserPermission(
callingUid, userId, true, false,
"registerLoadingProgressCallback");
final PackageSetting ps;
synchronized (mLock) {
ps = mSettings.mPackages.get(packageName);
if (ps == null) {
Slog.w(TAG, "Failed registering loading progress callback. Package "
+ packageName + " is not installed");
return false;
}
if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
Slog.w(TAG, "Failed registering loading progress callback. Package "
+ packageName + " is not visible to the calling app");
return false;
}
// TODO(b/165841827): return false if package is fully loaded
}
if (mIncrementalManager == null) {
Slog.w(TAG,
"Failed registering loading progress callback. Incremental is not enabled");
return false;
}
return mIncrementalManager.registerCallback(ps.getPathString(),
(IPackageLoadingProgressCallback) callback.getBinder());
}
@Override
public boolean unregisterInstalledLoadingProgressCallback(String packageName,
PackageManagerInternal.InstalledLoadingProgressCallback callback) {
final PackageSetting ps;
synchronized (mLock) {
ps = mSettings.mPackages.get(packageName);
if (ps == null) {
Slog.w(TAG, "Failed unregistering loading progress callback. Package "
+ packageName + " is not installed");
return false;
}
}
if (mIncrementalManager == null) {
return false;
}
return mIncrementalManager.unregisterCallback(ps.getPathString(),
(IPackageLoadingProgressCallback) callback.getBinder());
}
}
@GuardedBy("mLock")

View File

@@ -301,6 +301,20 @@ binder::Status BinderIncrementalService::waitForNativeBinariesExtraction(int sto
return ok();
}
binder::Status BinderIncrementalService::registerLoadingProgressListener(
int32_t storageId,
const ::android::sp<::android::os::incremental::IStorageLoadingProgressListener>&
progressListener,
bool* _aidl_return) {
*_aidl_return = mImpl.registerLoadingProgressListener(storageId, progressListener);
return ok();
}
binder::Status BinderIncrementalService::unregisterLoadingProgressListener(int32_t storageId,
bool* _aidl_return) {
*_aidl_return = mImpl.unregisterLoadingProgressListener(storageId);
return ok();
}
} // namespace android::os::incremental
jlong Incremental_IncrementalService_Start(JNIEnv* env) {

View File

@@ -81,6 +81,12 @@ public:
const std::string& abi, bool extractNativeLibs,
bool* _aidl_return) final;
binder::Status waitForNativeBinariesExtraction(int storageId, bool* _aidl_return) final;
binder::Status registerLoadingProgressListener(
int32_t storageId,
const ::android::sp<::android::os::incremental::IStorageLoadingProgressListener>&
progressListener,
bool* _aidl_return) final;
binder::Status unregisterLoadingProgressListener(int32_t storageId, bool* _aidl_return) final;
private:
android::incremental::IncrementalService mImpl;

View File

@@ -275,6 +275,7 @@ IncrementalService::IncrementalService(ServiceManagerWrapper&& sm, std::string_v
mJni(sm.getJni()),
mLooper(sm.getLooper()),
mTimedQueue(sm.getTimedQueue()),
mProgressUpdateJobQueue(sm.getProgressUpdateJobQueue()),
mFs(sm.getFs()),
mIncrementalDir(rootDir) {
CHECK(mVold) << "Vold service is unavailable";
@@ -283,6 +284,7 @@ IncrementalService::IncrementalService(ServiceManagerWrapper&& sm, std::string_v
CHECK(mJni) << "JNI is unavailable";
CHECK(mLooper) << "Looper is unavailable";
CHECK(mTimedQueue) << "TimedQueue is unavailable";
CHECK(mProgressUpdateJobQueue) << "mProgressUpdateJobQueue is unavailable";
CHECK(mFs) << "Fs is unavailable";
mJobQueue.reserve(16);
@@ -308,6 +310,7 @@ IncrementalService::~IncrementalService() {
mJobProcessor.join();
mCmdLooperThread.join();
mTimedQueue->stop();
mProgressUpdateJobQueue->stop();
// Ensure that mounts are destroyed while the service is still valid.
mBindsByPath.clear();
mMounts.clear();
@@ -1744,6 +1747,35 @@ float IncrementalService::getLoadingProgressFromPath(const IncFsMount& ifs,
return (float)filledBlocks / (float)totalBlocks;
}
bool IncrementalService::updateLoadingProgress(
StorageId storage, const StorageLoadingProgressListener& progressListener) {
const auto progress = getLoadingProgress(storage);
if (progress < 0) {
// Failed to get progress from incfs, abort.
return false;
}
progressListener->onStorageLoadingProgressChanged(storage, progress);
if (progress > 1 - 0.001f) {
// Stop updating progress once it is fully loaded
return true;
}
static constexpr auto kProgressUpdateInterval = 1000ms;
addTimedJob(*mProgressUpdateJobQueue, storage, kProgressUpdateInterval /* repeat after 1s */,
[storage, progressListener, this]() {
updateLoadingProgress(storage, progressListener);
});
return true;
}
bool IncrementalService::registerLoadingProgressListener(
StorageId storage, const StorageLoadingProgressListener& progressListener) {
return updateLoadingProgress(storage, progressListener);
}
bool IncrementalService::unregisterLoadingProgressListener(StorageId storage) {
return removeTimedJobs(*mProgressUpdateJobQueue, storage);
}
bool IncrementalService::perfLoggingEnabled() {
static const bool enabled = base::GetBoolProperty("incremental.perflogging", false);
return enabled;
@@ -1826,18 +1858,21 @@ void IncrementalService::onAppOpChanged(const std::string& packageName) {
}
}
void IncrementalService::addTimedJob(MountId id, Milliseconds after, Job what) {
bool IncrementalService::addTimedJob(TimedQueueWrapper& timedQueue, MountId id, Milliseconds after,
Job what) {
if (id == kInvalidStorageId) {
return;
return false;
}
mTimedQueue->addJob(id, after, std::move(what));
timedQueue.addJob(id, after, std::move(what));
return true;
}
void IncrementalService::removeTimedJobs(MountId id) {
bool IncrementalService::removeTimedJobs(TimedQueueWrapper& timedQueue, MountId id) {
if (id == kInvalidStorageId) {
return;
return false;
}
mTimedQueue->removeJobs(id);
timedQueue.removeJobs(id);
return true;
}
IncrementalService::DataLoaderStub::DataLoaderStub(IncrementalService& service, MountId id,
@@ -1879,7 +1914,7 @@ void IncrementalService::DataLoaderStub::cleanupResources() {
mHealthPath.clear();
unregisterFromPendingReads();
resetHealthControl();
mService.removeTimedJobs(mId);
mService.removeTimedJobs(*mService.mTimedQueue, mId);
}
requestDestroy();
@@ -2169,7 +2204,8 @@ void IncrementalService::DataLoaderStub::updateHealthStatus(bool baseline) {
}
LOG(DEBUG) << id() << ": updateHealthStatus in " << double(checkBackAfter.count()) / 1000.0
<< "secs";
mService.addTimedJob(id(), checkBackAfter, [this]() { updateHealthStatus(); });
mService.addTimedJob(*mService.mTimedQueue, id(), checkBackAfter,
[this]() { updateHealthStatus(); });
}
// With kTolerance we are expecting these to execute before the next update.

View File

@@ -22,6 +22,7 @@
#include <android/content/pm/IDataLoaderStatusListener.h>
#include <android/os/incremental/BnIncrementalServiceConnector.h>
#include <android/os/incremental/BnStorageHealthListener.h>
#include <android/os/incremental/BnStorageLoadingProgressListener.h>
#include <android/os/incremental/StorageHealthCheckParams.h>
#include <binder/IAppOpsCallback.h>
#include <utils/String16.h>
@@ -65,6 +66,8 @@ using DataLoaderStatusListener = ::android::sp<IDataLoaderStatusListener>;
using StorageHealthCheckParams = ::android::os::incremental::StorageHealthCheckParams;
using IStorageHealthListener = ::android::os::incremental::IStorageHealthListener;
using StorageHealthListener = ::android::sp<IStorageHealthListener>;
using IStorageLoadingProgressListener = ::android::os::incremental::IStorageLoadingProgressListener;
using StorageLoadingProgressListener = ::android::sp<IStorageLoadingProgressListener>;
class IncrementalService final {
public:
@@ -134,6 +137,9 @@ public:
int isFileFullyLoaded(StorageId storage, const std::string& path) const;
float getLoadingProgress(StorageId storage) const;
bool registerLoadingProgressListener(StorageId storage,
const StorageLoadingProgressListener& progressListener);
bool unregisterLoadingProgressListener(StorageId storage);
RawMetadata getMetadata(StorageId storage, std::string_view path) const;
RawMetadata getMetadata(StorageId storage, FileId node) const;
@@ -354,8 +360,10 @@ private:
void runCmdLooper();
void addTimedJob(MountId id, Milliseconds after, Job what);
void removeTimedJobs(MountId id);
bool addTimedJob(TimedQueueWrapper& timedQueue, MountId id, Milliseconds after, Job what);
bool removeTimedJobs(TimedQueueWrapper& timedQueue, MountId id);
bool updateLoadingProgress(int32_t storageId,
const StorageLoadingProgressListener& progressListener);
private:
const std::unique_ptr<VoldServiceWrapper> mVold;
@@ -365,6 +373,7 @@ private:
const std::unique_ptr<JniWrapper> mJni;
const std::unique_ptr<LooperWrapper> mLooper;
const std::unique_ptr<TimedQueueWrapper> mTimedQueue;
const std::unique_ptr<TimedQueueWrapper> mProgressUpdateJobQueue;
const std::unique_ptr<FsWrapper> mFs;
const std::string mIncrementalDir;

View File

@@ -357,6 +357,10 @@ std::unique_ptr<TimedQueueWrapper> RealServiceManager::getTimedQueue() {
return std::make_unique<RealTimedQueueWrapper>(mJvm);
}
std::unique_ptr<TimedQueueWrapper> RealServiceManager::getProgressUpdateJobQueue() {
return std::make_unique<RealTimedQueueWrapper>(mJvm);
}
std::unique_ptr<FsWrapper> RealServiceManager::getFs() {
return std::make_unique<RealFsWrapper>();
}

View File

@@ -154,6 +154,7 @@ public:
virtual std::unique_ptr<JniWrapper> getJni() = 0;
virtual std::unique_ptr<LooperWrapper> getLooper() = 0;
virtual std::unique_ptr<TimedQueueWrapper> getTimedQueue() = 0;
virtual std::unique_ptr<TimedQueueWrapper> getProgressUpdateJobQueue() = 0;
virtual std::unique_ptr<FsWrapper> getFs() = 0;
};
@@ -170,6 +171,7 @@ public:
std::unique_ptr<JniWrapper> getJni() final;
std::unique_ptr<LooperWrapper> getLooper() final;
std::unique_ptr<TimedQueueWrapper> getTimedQueue() final;
std::unique_ptr<TimedQueueWrapper> getProgressUpdateJobQueue() final;
std::unique_ptr<FsWrapper> getFs() final;
private:

View File

@@ -504,6 +504,14 @@ public:
int32_t mStatus = -1;
};
class MockStorageLoadingProgressListener : public IStorageLoadingProgressListener {
public:
MockStorageLoadingProgressListener() = default;
MOCK_METHOD2(onStorageLoadingProgressChanged,
binder::Status(int32_t storageId, float progress));
MOCK_METHOD0(onAsBinder, IBinder*());
};
class MockServiceManager : public ServiceManagerWrapper {
public:
MockServiceManager(std::unique_ptr<MockVoldService> vold,
@@ -513,6 +521,7 @@ public:
std::unique_ptr<MockJniWrapper> jni,
std::unique_ptr<MockLooperWrapper> looper,
std::unique_ptr<MockTimedQueueWrapper> timedQueue,
std::unique_ptr<MockTimedQueueWrapper> progressUpdateJobQueue,
std::unique_ptr<MockFsWrapper> fs)
: mVold(std::move(vold)),
mDataLoaderManager(std::move(dataLoaderManager)),
@@ -521,6 +530,7 @@ public:
mJni(std::move(jni)),
mLooper(std::move(looper)),
mTimedQueue(std::move(timedQueue)),
mProgressUpdateJobQueue(std::move(progressUpdateJobQueue)),
mFs(std::move(fs)) {}
std::unique_ptr<VoldServiceWrapper> getVoldService() final { return std::move(mVold); }
std::unique_ptr<DataLoaderManagerWrapper> getDataLoaderManager() final {
@@ -533,6 +543,9 @@ public:
std::unique_ptr<JniWrapper> getJni() final { return std::move(mJni); }
std::unique_ptr<LooperWrapper> getLooper() final { return std::move(mLooper); }
std::unique_ptr<TimedQueueWrapper> getTimedQueue() final { return std::move(mTimedQueue); }
std::unique_ptr<TimedQueueWrapper> getProgressUpdateJobQueue() final {
return std::move(mProgressUpdateJobQueue);
}
std::unique_ptr<FsWrapper> getFs() final { return std::move(mFs); }
private:
@@ -543,6 +556,7 @@ private:
std::unique_ptr<MockJniWrapper> mJni;
std::unique_ptr<MockLooperWrapper> mLooper;
std::unique_ptr<MockTimedQueueWrapper> mTimedQueue;
std::unique_ptr<MockTimedQueueWrapper> mProgressUpdateJobQueue;
std::unique_ptr<MockFsWrapper> mFs;
};
@@ -567,19 +581,19 @@ public:
mLooper = looper.get();
auto timedQueue = std::make_unique<NiceMock<MockTimedQueueWrapper>>();
mTimedQueue = timedQueue.get();
auto progressUpdateJobQueue = std::make_unique<NiceMock<MockTimedQueueWrapper>>();
mProgressUpdateJobQueue = progressUpdateJobQueue.get();
auto fs = std::make_unique<NiceMock<MockFsWrapper>>();
mFs = fs.get();
mIncrementalService =
std::make_unique<IncrementalService>(MockServiceManager(std::move(vold),
std::move(
dataloaderManager),
std::move(incFs),
std::move(appOps),
std::move(jni),
std::move(looper),
std::move(timedQueue),
std::move(fs)),
mRootDir.path);
mIncrementalService = std::make_unique<
IncrementalService>(MockServiceManager(std::move(vold),
std::move(dataloaderManager),
std::move(incFs), std::move(appOps),
std::move(jni), std::move(looper),
std::move(timedQueue),
std::move(progressUpdateJobQueue),
std::move(fs)),
mRootDir.path);
mDataLoaderParcel.packageName = "com.test";
mDataLoaderParcel.arguments = "uri";
mDataLoaderManager->unbindFromDataLoaderSuccess();
@@ -624,6 +638,7 @@ protected:
NiceMock<MockJniWrapper>* mJni = nullptr;
NiceMock<MockLooperWrapper>* mLooper = nullptr;
NiceMock<MockTimedQueueWrapper>* mTimedQueue = nullptr;
NiceMock<MockTimedQueueWrapper>* mProgressUpdateJobQueue = nullptr;
NiceMock<MockFsWrapper>* mFs = nullptr;
NiceMock<MockDataLoader>* mDataLoader = nullptr;
std::unique_ptr<IncrementalService> mIncrementalService;
@@ -1166,4 +1181,44 @@ TEST_F(IncrementalServiceTest, testGetLoadingProgressSuccess) {
EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(3);
ASSERT_EQ(0.5, mIncrementalService->getLoadingProgress(storageId));
}
TEST_F(IncrementalServiceTest, testRegisterLoadingProgressListenerSuccess) {
mIncFs->countFilledBlocksSuccess();
mFs->hasFiles();
TemporaryDir tempDir;
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew,
{}, {}, {});
sp<NiceMock<MockStorageLoadingProgressListener>> listener{
new NiceMock<MockStorageLoadingProgressListener>};
NiceMock<MockStorageLoadingProgressListener>* listenerMock = listener.get();
EXPECT_CALL(*listenerMock, onStorageLoadingProgressChanged(_, _)).Times(2);
EXPECT_CALL(*mProgressUpdateJobQueue, addJob(_, _, _)).Times(2);
mIncrementalService->registerLoadingProgressListener(storageId, listener);
// Timed callback present.
ASSERT_EQ(storageId, mProgressUpdateJobQueue->mId);
ASSERT_EQ(mProgressUpdateJobQueue->mAfter, 1000ms);
auto timedCallback = mProgressUpdateJobQueue->mWhat;
timedCallback();
ASSERT_EQ(storageId, mProgressUpdateJobQueue->mId);
ASSERT_EQ(mProgressUpdateJobQueue->mAfter, 1000ms);
mIncrementalService->unregisterLoadingProgressListener(storageId);
ASSERT_EQ(mProgressUpdateJobQueue->mAfter, Milliseconds{});
}
TEST_F(IncrementalServiceTest, testRegisterLoadingProgressListenerFailsToGetProgress) {
mIncFs->countFilledBlocksFails();
mFs->hasFiles();
TemporaryDir tempDir;
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew,
{}, {}, {});
sp<NiceMock<MockStorageLoadingProgressListener>> listener{
new NiceMock<MockStorageLoadingProgressListener>};
NiceMock<MockStorageLoadingProgressListener>* listenerMock = listener.get();
EXPECT_CALL(*listenerMock, onStorageLoadingProgressChanged(_, _)).Times(0);
mIncrementalService->registerLoadingProgressListener(storageId, listener);
}
} // namespace android::os::incremental