[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:
@@ -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",
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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>();
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user