Disallow read logs collection if user changes their mind.

Bug: b/152633648
Test: atest PackageManagerShellCommandTest PackageManagerShellCommandIncrementalTest IncrementalServiceTest
Test: adb shell appops set 1000 GET_USAGE_STATS deny
Change-Id: I7fc8356f84fe30669483470579eedf546f81f297
diff --git a/services/incremental/Android.bp b/services/incremental/Android.bp
index b13d330..02bb0bc 100644
--- a/services/incremental/Android.bp
+++ b/services/incremental/Android.bp
@@ -50,6 +50,7 @@
         "libbinder",
         "libcrypto",
         "libcutils",
+        "libdataloader",
         "libincfs",
         "liblog",
         "libz",
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 7349cf6..0da1673 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "IncrementalService"
 
 #include "IncrementalService.h"
+#include "IncrementalServiceValidation.h"
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
@@ -50,6 +51,9 @@
 using namespace android::content::pm;
 namespace fs = std::filesystem;
 
+constexpr const char* kDataUsageStats = "android.permission.LOADER_USAGE_STATS";
+constexpr const char* kOpUsage = "android:get_usage_stats";
+
 namespace android::incremental {
 
 namespace {
@@ -232,6 +236,7 @@
       : mVold(sm.getVoldService()),
         mDataLoaderManager(sm.getDataLoaderManager()),
         mIncFs(sm.getIncFs()),
+        mAppOpsManager(sm.getAppOpsManager()),
         mIncrementalDir(rootDir) {
     if (!mVold) {
         LOG(FATAL) << "Vold service is unavailable";
@@ -239,6 +244,9 @@
     if (!mDataLoaderManager) {
         LOG(FATAL) << "DataLoaderManagerService is unavailable";
     }
+    if (!mAppOpsManager) {
+        LOG(FATAL) << "AppOpsManager is unavailable";
+    }
     mountExistingImages();
 }
 
@@ -279,12 +287,9 @@
         dprintf(fd, "\t\troot: %s\n", mnt.root.c_str());
         dprintf(fd, "\t\tnextStorageDirNo: %d\n", mnt.nextStorageDirNo.load());
         dprintf(fd, "\t\tdataLoaderStatus: %d\n", mnt.dataLoaderStatus.load());
-        dprintf(fd, "\t\tconnectionLostTime: %s\n", toString(mnt.connectionLostTime));
-        dprintf(fd, "\t\tsavedDataLoaderParams:\n");
-        if (!mnt.savedDataLoaderParams) {
-            dprintf(fd, "\t\t\tnone\n");
-        } else {
-            const auto& params = mnt.savedDataLoaderParams.value();
+        {
+            const auto& params = mnt.dataLoaderParams;
+            dprintf(fd, "\t\tdataLoaderParams:\n");
             dprintf(fd, "\t\t\ttype: %s\n", toString(params.type).c_str());
             dprintf(fd, "\t\t\tpackageName: %s\n", params.packageName.c_str());
             dprintf(fd, "\t\t\tclassName: %s\n", params.className.c_str());
@@ -332,6 +337,7 @@
     }
 
     std::thread([this, mounts = std::move(mounts)]() {
+        /* TODO(b/151241369): restore data loaders on reboot.
         for (auto&& ifs : mounts) {
             if (prepareDataLoader(*ifs)) {
                 LOG(INFO) << "Successfully started data loader for mount " << ifs->mountId;
@@ -340,6 +346,7 @@
                 LOG(WARNING) << "Failed to start data loader for mount " << ifs->mountId;
             }
         }
+        */
         mPrepareDataLoaders.set_value_at_thread_exit();
     }).detach();
     return mPrepareDataLoaders.get_future();
@@ -459,13 +466,15 @@
         return kInvalidStorageId;
     }
 
+    ifs->dataLoaderParams = std::move(dataLoaderParams);
+
     {
         metadata::Mount m;
         m.mutable_storage()->set_id(ifs->mountId);
-        m.mutable_loader()->set_type((int)dataLoaderParams.type);
-        m.mutable_loader()->set_package_name(dataLoaderParams.packageName);
-        m.mutable_loader()->set_class_name(dataLoaderParams.className);
-        m.mutable_loader()->set_arguments(dataLoaderParams.arguments);
+        m.mutable_loader()->set_type((int)ifs->dataLoaderParams.type);
+        m.mutable_loader()->set_package_name(ifs->dataLoaderParams.packageName);
+        m.mutable_loader()->set_class_name(ifs->dataLoaderParams.className);
+        m.mutable_loader()->set_arguments(ifs->dataLoaderParams.arguments);
         const auto metadata = m.SerializeAsString();
         m.mutable_loader()->release_arguments();
         m.mutable_loader()->release_class_name();
@@ -493,7 +502,7 @@
     // Done here as well, all data structures are in good state.
     secondCleanupOnFailure.release();
 
-    if (!prepareDataLoader(*ifs, &dataLoaderParams, &dataLoaderStatusListener)) {
+    if (!prepareDataLoader(*ifs, &dataLoaderStatusListener)) {
         LOG(ERROR) << "prepareDataLoader() failed";
         deleteStorageLocked(*ifs, std::move(l));
         return kInvalidStorageId;
@@ -573,11 +582,30 @@
         return -EINVAL;
     }
 
+    ifs->dataLoaderFilesystemParams.readLogsEnabled = enableReadLogs;
+    if (enableReadLogs) {
+        // We never unregister the callbacks, but given a restricted number of data loaders and even fewer asking for read log access, should be ok.
+        registerAppOpsCallback(ifs->dataLoaderParams.packageName);
+    }
+
+    return applyStorageParams(*ifs);
+}
+
+int IncrementalService::applyStorageParams(IncFsMount& ifs) {
+    const bool enableReadLogs = ifs.dataLoaderFilesystemParams.readLogsEnabled;
+    if (enableReadLogs) {
+        if (auto status = CheckPermissionForDataDelivery(kDataUsageStats, kOpUsage);
+            !status.isOk()) {
+            LOG(ERROR) << "CheckPermissionForDataDelivery failed: " << status.toString8();
+            return fromBinderStatus(status);
+        }
+    }
+
     using unique_fd = ::android::base::unique_fd;
     ::android::os::incremental::IncrementalFileSystemControlParcel control;
-    control.cmd.reset(unique_fd(dup(ifs->control.cmd())));
-    control.pendingReads.reset(unique_fd(dup(ifs->control.pendingReads())));
-    auto logsFd = ifs->control.logs();
+    control.cmd.reset(unique_fd(dup(ifs.control.cmd())));
+    control.pendingReads.reset(unique_fd(dup(ifs.control.pendingReads())));
+    auto logsFd = ifs.control.logs();
     if (logsFd >= 0) {
         control.log.reset(unique_fd(dup(logsFd)));
     }
@@ -586,12 +614,7 @@
     const auto status = mVold->setIncFsMountOptions(control, enableReadLogs);
     if (!status.isOk()) {
         LOG(ERROR) << "Calling Vold::setIncFsMountOptions() failed: " << status.toString8();
-        return status.exceptionCode() == binder::Status::EX_SERVICE_SPECIFIC
-                ? status.serviceSpecificErrorCode() > 0 ? -status.serviceSpecificErrorCode()
-                                                        : status.serviceSpecificErrorCode() == 0
-                                ? -EFAULT
-                                : status.serviceSpecificErrorCode()
-                : -EIO;
+        return fromBinderStatus(status);
     }
 
     return 0;
@@ -1015,16 +1038,26 @@
 
     auto ifs = std::make_shared<IncFsMount>(std::string(root), -1, std::move(control), *this);
 
-    auto m = parseFromIncfs<metadata::Mount>(mIncFs.get(), ifs->control,
-                                             path::join(mountTarget, constants().infoMdName));
-    if (!m.has_loader() || !m.has_storage()) {
+    auto mount = parseFromIncfs<metadata::Mount>(mIncFs.get(), ifs->control,
+                                                 path::join(mountTarget, constants().infoMdName));
+    if (!mount.has_loader() || !mount.has_storage()) {
         LOG(ERROR) << "Bad mount metadata in mount at " << root;
         return false;
     }
 
-    ifs->mountId = m.storage().id();
+    ifs->mountId = mount.storage().id();
     mNextId = std::max(mNextId, ifs->mountId + 1);
 
+    // DataLoader params
+    {
+        auto& dlp = ifs->dataLoaderParams;
+        const auto& loader = mount.loader();
+        dlp.type = (android::content::pm::DataLoaderType)loader.type();
+        dlp.packageName = loader.package_name();
+        dlp.className = loader.class_name();
+        dlp.arguments = loader.arguments();
+    }
+
     std::vector<std::pair<std::string, metadata::BindPoint>> bindPoints;
     auto d = openDir(path::c_str(mountTarget));
     while (auto e = ::readdir(d.get())) {
@@ -1095,23 +1128,9 @@
 }
 
 bool IncrementalService::prepareDataLoader(IncrementalService::IncFsMount& ifs,
-                                           DataLoaderParamsParcel* params,
                                            const DataLoaderStatusListener* externalListener) {
     if (!mSystemReady.load(std::memory_order_relaxed)) {
         std::unique_lock l(ifs.lock);
-        if (params) {
-            if (ifs.savedDataLoaderParams) {
-                LOG(WARNING) << "Trying to pass second set of data loader parameters, ignored it";
-            } else {
-                ifs.savedDataLoaderParams = std::move(*params);
-            }
-        } else {
-            if (!ifs.savedDataLoaderParams) {
-                LOG(ERROR) << "Mount " << ifs.mountId
-                           << " is broken: no data loader params (system is not ready yet)";
-                return false;
-            }
-        }
         return true; // eventually...
     }
 
@@ -1121,12 +1140,6 @@
         return true;
     }
 
-    auto* dlp = params ? params
-                       : ifs.savedDataLoaderParams ? &ifs.savedDataLoaderParams.value() : nullptr;
-    if (!dlp) {
-        LOG(ERROR) << "Mount " << ifs.mountId << " is broken: no data loader params";
-        return false;
-    }
     FileSystemControlParcel fsControlParcel;
     fsControlParcel.incremental = aidl::make_nullable<IncrementalFileSystemControlParcel>();
     fsControlParcel.incremental->cmd.reset(base::unique_fd(::dup(ifs.control.cmd())));
@@ -1138,13 +1151,11 @@
                                               externalListener ? *externalListener
                                                                : DataLoaderStatusListener());
     bool created = false;
-    auto status = mDataLoaderManager->initializeDataLoader(ifs.mountId, *dlp, fsControlParcel,
-                                                           listener, &created);
+    auto status = mDataLoaderManager->initializeDataLoader(ifs.mountId, ifs.dataLoaderParams, fsControlParcel, listener, &created);
     if (!status.isOk() || !created) {
         LOG(ERROR) << "Failed to create a data loader for mount " << ifs.mountId;
         return false;
     }
-    ifs.savedDataLoaderParams.reset();
     return true;
 }
 
@@ -1268,6 +1279,42 @@
     return success;
 }
 
+void IncrementalService::registerAppOpsCallback(const std::string& packageName) {
+    if (packageName.empty()) {
+        return;
+    }
+
+    {
+        std::unique_lock lock{mCallbacksLock};
+        if (!mCallbackRegistered.insert(packageName).second) {
+            return;
+        }
+    }
+
+    /* TODO(b/152633648): restore callback after it's not crashing Binder anymore.
+    sp<AppOpsListener> listener = new AppOpsListener(*this, packageName);
+    mAppOpsManager->startWatchingMode(AppOpsManager::OP_GET_USAGE_STATS, String16(packageName.c_str()), listener);
+    */
+}
+
+void IncrementalService::onAppOppChanged(const std::string& packageName) {
+    std::vector<IfsMountPtr> affected;
+    {
+        std::lock_guard l(mLock);
+        affected.reserve(mMounts.size());
+        for (auto&& [id, ifs] : mMounts) {
+            if (ifs->dataLoaderFilesystemParams.readLogsEnabled && ifs->dataLoaderParams.packageName == packageName) {
+                affected.push_back(ifs);
+            }
+        }
+    }
+    /* TODO(b/152633648): restore callback after it's not crashing Kernel anymore.
+    for (auto&& ifs : affected) {
+        applyStorageParams(*ifs);
+    }
+    */
+}
+
 binder::Status IncrementalService::IncrementalDataLoaderListener::onStatusChanged(MountId mountId,
                                                                                   int newStatus) {
     if (externalListener) {
@@ -1331,4 +1378,8 @@
     return binder::Status::ok();
 }
 
+void IncrementalService::AppOpsListener::opChanged(int32_t op, const String16&) {
+    incrementalService.onAppOppChanged(packageName);
+}
+
 } // namespace android::incremental
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index 3615314..ff69633 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -40,6 +40,7 @@
 #include "ServiceWrappers.h"
 #include "android/content/pm/BnDataLoaderStatusListener.h"
 #include "incfs.h"
+#include "dataloader_ndk.h"
 #include "path.h"
 
 using namespace android::os::incremental;
@@ -132,6 +133,7 @@
     bool startLoading(StorageId storage) const;
     bool configureNativeBinaries(StorageId storage, std::string_view apkFullPath,
                                  std::string_view libDirRelativePath, std::string_view abi);
+
     class IncrementalDataLoaderListener : public android::content::pm::BnDataLoaderStatusListener {
     public:
         IncrementalDataLoaderListener(IncrementalService& incrementalService,
@@ -145,6 +147,16 @@
         DataLoaderStatusListener externalListener;
     };
 
+    class AppOpsListener : public android::BnAppOpsCallback {
+    public:
+        AppOpsListener(IncrementalService& incrementalService, std::string packageName) : incrementalService(incrementalService), packageName(std::move(packageName)) {}
+        void opChanged(int32_t op, const String16& packageName) override;
+
+    private:
+        IncrementalService& incrementalService;
+        const std::string packageName;
+    };
+
 private:
     struct IncFsMount {
         struct Bind {
@@ -169,11 +181,11 @@
         /*const*/ MountId mountId;
         StorageMap storages;
         BindMap bindPoints;
-        std::optional<DataLoaderParamsParcel> savedDataLoaderParams;
+        DataLoaderParamsParcel dataLoaderParams;
+        DataLoaderFilesystemParams dataLoaderFilesystemParams;
         std::atomic<int> nextStorageDirNo{0};
         std::atomic<int> dataLoaderStatus = -1;
         bool dataLoaderStartRequested = false;
-        TimePoint connectionLostTime = TimePoint();
         const IncrementalService& incrementalService;
 
         IncFsMount(std::string root, MountId mountId, Control control,
@@ -181,7 +193,9 @@
               : root(std::move(root)),
                 control(std::move(control)),
                 mountId(mountId),
-                incrementalService(incrementalService) {}
+                incrementalService(incrementalService) {
+            dataLoaderFilesystemParams.readLogsEnabled = false;
+        }
         IncFsMount(IncFsMount&&) = delete;
         IncFsMount& operator=(IncFsMount&&) = delete;
         ~IncFsMount();
@@ -208,8 +222,7 @@
                            std::string&& source, std::string&& target, BindKind kind,
                            std::unique_lock<std::mutex>& mainLock);
 
-    bool prepareDataLoader(IncFsMount& ifs, DataLoaderParamsParcel* params = nullptr,
-                           const DataLoaderStatusListener* externalListener = nullptr);
+    bool prepareDataLoader(IncFsMount& ifs, const DataLoaderStatusListener* externalListener = nullptr);
     bool startDataLoader(MountId mountId) const;
 
     BindPathMap::const_iterator findStorageLocked(std::string_view path) const;
@@ -221,10 +234,16 @@
     std::string normalizePathToStorage(const IfsMountPtr incfs, StorageId storage,
                                        std::string_view path);
 
+    int applyStorageParams(IncFsMount& ifs);
+
+    void registerAppOpsCallback(const std::string& packageName);
+    void onAppOppChanged(const std::string& packageName);
+
     // Member variables
-    std::unique_ptr<VoldServiceWrapper> mVold;
-    std::unique_ptr<DataLoaderManagerWrapper> mDataLoaderManager;
-    std::unique_ptr<IncFsWrapper> mIncFs;
+    std::unique_ptr<VoldServiceWrapper> const mVold;
+    std::unique_ptr<DataLoaderManagerWrapper> const mDataLoaderManager;
+    std::unique_ptr<IncFsWrapper> const mIncFs;
+    std::unique_ptr<AppOpsManagerWrapper> const mAppOpsManager;
     const std::string mIncrementalDir;
 
     mutable std::mutex mLock;
@@ -232,6 +251,9 @@
     MountMap mMounts;
     BindPathMap mBindsByPath;
 
+    std::mutex mCallbacksLock;
+    std::set<std::string> mCallbackRegistered;
+
     std::atomic_bool mSystemReady = false;
     StorageId mNextId = 0;
     std::promise<void> mPrepareDataLoaders;
diff --git a/services/incremental/IncrementalServiceValidation.h b/services/incremental/IncrementalServiceValidation.h
new file mode 100644
index 0000000..24f9f7f
--- /dev/null
+++ b/services/incremental/IncrementalServiceValidation.h
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <android-base/stringprintf.h>
+#include <binder/IPCThreadState.h>
+#include <binder/PermissionCache.h>
+#include <binder/PermissionController.h>
+#include <binder/Status.h>
+
+namespace android::incremental {
+
+inline binder::Status Ok() {
+    return binder::Status::ok();
+}
+
+inline binder::Status Exception(uint32_t code, const std::string& msg) {
+    return binder::Status::fromExceptionCode(code, String8(msg.c_str()));
+}
+
+inline int fromBinderStatus(const binder::Status& status) {
+    return status.exceptionCode() == binder::Status::EX_SERVICE_SPECIFIC
+            ? status.serviceSpecificErrorCode() > 0 ? -status.serviceSpecificErrorCode()
+                                                    : status.serviceSpecificErrorCode() == 0
+                            ? -EFAULT
+                            : status.serviceSpecificErrorCode()
+            : -EIO;
+}
+
+inline binder::Status CheckPermissionForDataDelivery(const char* permission, const char* operation) {
+    using android::base::StringPrintf;
+
+    int32_t pid;
+    int32_t uid;
+
+    if (!PermissionCache::checkCallingPermission(String16(permission), &pid, &uid)) {
+        return Exception(binder::Status::EX_SECURITY,
+                         StringPrintf("UID %d / PID %d lacks permission %s", uid, pid, permission));
+    }
+
+    // Caller must also have op granted.
+    PermissionController pc;
+    // Package is a required parameter. Need to obtain one.
+    Vector<String16> packages;
+    pc.getPackagesForUid(uid, packages);
+    if (packages.empty()) {
+        return Exception(binder::Status::EX_SECURITY,
+                         StringPrintf("UID %d / PID %d has no packages", uid, pid));
+    }
+    switch (auto result = pc.noteOp(String16(operation), uid, packages[0]); result) {
+        case PermissionController::MODE_ALLOWED:
+        case PermissionController::MODE_DEFAULT:
+            return binder::Status::ok();
+        default:
+            return Exception(binder::Status::EX_SECURITY,
+                             StringPrintf("UID %d / PID %d lacks app-op %s, error %d", uid, pid,
+                                          operation, result));
+    }
+}
+
+} // namespace android::incremental
diff --git a/services/incremental/ServiceWrappers.cpp b/services/incremental/ServiceWrappers.cpp
index 2e31ef1..9f4192f 100644
--- a/services/incremental/ServiceWrappers.cpp
+++ b/services/incremental/ServiceWrappers.cpp
@@ -59,4 +59,8 @@
     return std::make_unique<RealIncFs>();
 }
 
+std::unique_ptr<AppOpsManagerWrapper> RealServiceManager::getAppOpsManager() {
+    return std::make_unique<RealAppOpsManager>();
+}
+
 } // namespace android::os::incremental
diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h
index c330030..449b457 100644
--- a/services/incremental/ServiceWrappers.h
+++ b/services/incremental/ServiceWrappers.h
@@ -24,6 +24,7 @@
 #include <android/content/pm/IDataLoaderManager.h>
 #include <android/content/pm/IDataLoaderStatusListener.h>
 #include <android/os/IVold.h>
+#include <binder/AppOpsManager.h>
 #include <binder/IServiceManager.h>
 #include <incfs.h>
 
@@ -81,12 +82,19 @@
     virtual ErrorCode writeBlocks(Span<const DataBlock> blocks) const = 0;
 };
 
+class AppOpsManagerWrapper {
+public:
+    virtual ~AppOpsManagerWrapper() = default;
+    virtual void startWatchingMode(int32_t op, const String16& packageName, const sp<IAppOpsCallback>& callback) = 0;
+};
+
 class ServiceManagerWrapper {
 public:
     virtual ~ServiceManagerWrapper() = default;
     virtual std::unique_ptr<VoldServiceWrapper> getVoldService() = 0;
     virtual std::unique_ptr<DataLoaderManagerWrapper> getDataLoaderManager() = 0;
     virtual std::unique_ptr<IncFsWrapper> getIncFs() = 0;
+    virtual std::unique_ptr<AppOpsManagerWrapper> getAppOpsManager() = 0;
 };
 
 // --- Real stuff ---
@@ -137,13 +145,24 @@
     sp<content::pm::IDataLoaderManager> mInterface;
 };
 
+class RealAppOpsManager : public AppOpsManagerWrapper {
+public:
+    ~RealAppOpsManager() = default;
+    void startWatchingMode(int32_t op, const String16& packageName, const sp<IAppOpsCallback>& callback) override {
+        mAppOpsManager.startWatchingMode(op, packageName, callback);
+    }
+private:
+    android::AppOpsManager mAppOpsManager;
+};
+
 class RealServiceManager : public ServiceManagerWrapper {
 public:
     RealServiceManager(sp<IServiceManager> serviceManager);
     ~RealServiceManager() = default;
-    std::unique_ptr<VoldServiceWrapper> getVoldService() override;
-    std::unique_ptr<DataLoaderManagerWrapper> getDataLoaderManager() override;
-    std::unique_ptr<IncFsWrapper> getIncFs() override;
+    std::unique_ptr<VoldServiceWrapper> getVoldService() final;
+    std::unique_ptr<DataLoaderManagerWrapper> getDataLoaderManager() final;
+    std::unique_ptr<IncFsWrapper> getIncFs() final;
+    std::unique_ptr<AppOpsManagerWrapper> getAppOpsManager() final;
 
 private:
     template <class INTERFACE>
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index cde38fb..5553f68 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -220,24 +220,32 @@
     }
 };
 
+class MockAppOpsManager : public AppOpsManagerWrapper {
+    MOCK_METHOD3(startWatchingMode, void(int32_t, const String16&, const sp<IAppOpsCallback>&));
+};
+
 class MockServiceManager : public ServiceManagerWrapper {
 public:
     MockServiceManager(std::unique_ptr<MockVoldService> vold,
                        std::unique_ptr<MockDataLoaderManager> manager,
-                       std::unique_ptr<MockIncFs> incfs)
+                       std::unique_ptr<MockIncFs> incfs,
+                       std::unique_ptr<MockAppOpsManager> appOpsManager)
           : mVold(std::move(vold)),
             mDataLoaderManager(std::move(manager)),
-            mIncFs(std::move(incfs)) {}
+            mIncFs(std::move(incfs)),
+            mAppOpsManager(std::move(appOpsManager)) {}
     std::unique_ptr<VoldServiceWrapper> getVoldService() final { return std::move(mVold); }
     std::unique_ptr<DataLoaderManagerWrapper> getDataLoaderManager() final {
         return std::move(mDataLoaderManager);
     }
     std::unique_ptr<IncFsWrapper> getIncFs() final { return std::move(mIncFs); }
+    std::unique_ptr<AppOpsManagerWrapper> getAppOpsManager() final { return std::move(mAppOpsManager); }
 
 private:
     std::unique_ptr<MockVoldService> mVold;
     std::unique_ptr<MockDataLoaderManager> mDataLoaderManager;
     std::unique_ptr<MockIncFs> mIncFs;
+    std::unique_ptr<MockAppOpsManager> mAppOpsManager;
 };
 
 // --- IncrementalServiceTest ---
@@ -251,11 +259,13 @@
         mDataLoaderManager = dataloaderManager.get();
         auto incFs = std::make_unique<NiceMock<MockIncFs>>();
         mIncFs = incFs.get();
+        auto appOps = std::make_unique<NiceMock<MockAppOpsManager>>();
+        mAppOpsManager = appOps.get();
         mIncrementalService =
                 std::make_unique<IncrementalService>(MockServiceManager(std::move(vold),
-                                                                        std::move(
-                                                                                dataloaderManager),
-                                                                        std::move(incFs)),
+                                                                        std::move(dataloaderManager),
+                                                                        std::move(incFs),
+                                                                        std::move(appOps)),
                                                      mRootDir.path);
         mDataLoaderParcel.packageName = "com.test";
         mDataLoaderParcel.arguments = "uri";
@@ -287,6 +297,7 @@
     NiceMock<MockVoldService>* mVold;
     NiceMock<MockIncFs>* mIncFs;
     NiceMock<MockDataLoaderManager>* mDataLoaderManager;
+    NiceMock<MockAppOpsManager>* mAppOpsManager;
     std::unique_ptr<IncrementalService> mIncrementalService;
     TemporaryDir mRootDir;
     DataLoaderParamsParcel mDataLoaderParcel;