Loading services/core/java/com/android/server/pm/InstallDependencyHelper.java +203 −14 Original line number Diff line number Diff line Loading @@ -17,51 +17,240 @@ package com.android.server.pm; import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY; import static android.os.Process.SYSTEM_UID; import android.annotation.NonNull; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ResolveInfo; import android.content.pm.SharedLibraryInfo; import android.content.pm.dependencyinstaller.DependencyInstallerCallback; import android.content.pm.dependencyinstaller.IDependencyInstallerCallback; import android.content.pm.dependencyinstaller.IDependencyInstallerService; import android.content.pm.parsing.PackageLite; import android.os.Handler; import android.os.OutcomeReceiver; import android.os.Process; import android.os.RemoteException; import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.infra.AndroidFuture; import com.android.internal.infra.ServiceConnector; import java.util.List; import java.util.concurrent.TimeUnit; /** * Helper class to interact with SDK Dependency Installer service. */ public class InstallDependencyHelper { private static final String TAG = InstallDependencyHelper.class.getSimpleName(); private static final boolean DEBUG = true; private static final String ACTION_INSTALL_DEPENDENCY = "android.intent.action.INSTALL_DEPENDENCY"; // The maximum amount of time to wait before the system unbinds from the verifier. private static final long UNBIND_TIMEOUT_MILLIS = TimeUnit.HOURS.toMillis(6); private static final long REQUEST_TIMEOUT_MILLIS = TimeUnit.MINUTES.toMillis(1); private final SharedLibrariesImpl mSharedLibraries; private final Context mContext; private final Object mRemoteServiceLock = new Object(); InstallDependencyHelper(SharedLibrariesImpl sharedLibraries) { @GuardedBy("mRemoteServiceLock") private ServiceConnector<IDependencyInstallerService> mRemoteService = null; InstallDependencyHelper(Context context, SharedLibrariesImpl sharedLibraries) { mContext = context; mSharedLibraries = sharedLibraries; } void resolveLibraryDependenciesIfNeeded(PackageLite pkg, OutcomeReceiver<Void, PackageManagerException> callback) { final List<SharedLibraryInfo> missing; void resolveLibraryDependenciesIfNeeded(PackageLite pkg, Computer snapshot, int userId, Handler handler, OutcomeReceiver<Void, PackageManagerException> origCallback) { CallOnceProxy callback = new CallOnceProxy(handler, origCallback); try { missing = mSharedLibraries.collectMissingSharedLibraryInfos(pkg); resolveLibraryDependenciesIfNeededInternal(pkg, snapshot, userId, handler, callback); } catch (PackageManagerException e) { callback.onError(e); return; } catch (Exception e) { onError(callback, e.getMessage()); } } private void resolveLibraryDependenciesIfNeededInternal(PackageLite pkg, Computer snapshot, int userId, Handler handler, CallOnceProxy callback) throws PackageManagerException { final List<SharedLibraryInfo> missing = mSharedLibraries.collectMissingSharedLibraryInfos(pkg); if (missing.isEmpty()) { if (DEBUG) { Slog.i(TAG, "No missing dependency for " + pkg); } // No need for dependency resolution. Move to installation directly. callback.onResult(null); return; } try { bindToDependencyInstaller(); } catch (Exception e) { if (!bindToDependencyInstallerIfNeeded(userId, handler, snapshot)) { onError(callback, "Dependency Installer Service not found"); return; } IDependencyInstallerCallback serviceCallback = new IDependencyInstallerCallback.Stub() { @Override public void onAllDependenciesResolved(int[] sessionIds) throws RemoteException { // TODO(b/372862145): Implement waiting for sessions to finish installation callback.onResult(null); } @Override public void onFailureToResolveAllDependencies() throws RemoteException { onError(callback, "Failed to resolve all dependencies automatically"); } }; boolean scheduleSuccess; synchronized (mRemoteServiceLock) { scheduleSuccess = mRemoteService.run(service -> { service.onDependenciesRequired(missing, new DependencyInstallerCallback(serviceCallback.asBinder())); }); } if (!scheduleSuccess) { onError(callback, "Failed to schedule job on Dependency Installer Service"); } } private void onError(CallOnceProxy callback, String msg) { PackageManagerException pe = new PackageManagerException( INSTALL_FAILED_MISSING_SHARED_LIBRARY, e.getMessage()); INSTALL_FAILED_MISSING_SHARED_LIBRARY, msg); callback.onError(pe); } private boolean bindToDependencyInstallerIfNeeded(int userId, Handler handler, Computer snapshot) { synchronized (mRemoteServiceLock) { if (mRemoteService != null) { if (DEBUG) { Slog.i(TAG, "DependencyInstallerService already bound"); } return true; } } private void bindToDependencyInstaller() { throw new IllegalStateException("Failed to bind to Dependency Installer"); Intent serviceIntent = new Intent(ACTION_INSTALL_DEPENDENCY); // TODO(b/372862145): Use RoleManager to find the package name List<ResolveInfo> resolvedIntents = snapshot.queryIntentServicesInternal( serviceIntent, /*resolvedType=*/ null, /*flags=*/0, userId, SYSTEM_UID, Process.INVALID_PID, /*includeInstantApps*/ false, /*resolveForStart*/ false); if (resolvedIntents.isEmpty()) { return false; } ResolveInfo resolveInfo = resolvedIntents.getFirst(); ComponentName componentName = resolveInfo.getComponentInfo().getComponentName(); serviceIntent.setComponent(componentName); ServiceConnector<IDependencyInstallerService> serviceConnector = new ServiceConnector.Impl<IDependencyInstallerService>(mContext, serviceIntent, Context.BIND_AUTO_CREATE, userId, IDependencyInstallerService.Stub::asInterface) { @Override protected Handler getJobHandler() { return handler; } @Override protected long getRequestTimeoutMs() { return REQUEST_TIMEOUT_MILLIS; } @Override protected long getAutoDisconnectTimeoutMs() { return UNBIND_TIMEOUT_MILLIS; } }; synchronized (mRemoteServiceLock) { // Some other thread managed to connect to the service first if (mRemoteService != null) { return true; } mRemoteService = serviceConnector; mRemoteService.setServiceLifecycleCallbacks( new ServiceConnector.ServiceLifecycleCallbacks<>() { @Override public void onDisconnected(@NonNull IDependencyInstallerService service) { Slog.w(TAG, "DependencyInstallerService " + componentName + " is disconnected"); destroy(); } @Override public void onBinderDied() { Slog.w(TAG, "DependencyInstallerService " + componentName + " has died"); destroy(); } private void destroy() { synchronized (mRemoteServiceLock) { if (mRemoteService != null) { mRemoteService.unbind(); mRemoteService = null; } } } }); AndroidFuture<IDependencyInstallerService> unusedFuture = mRemoteService.connect(); } return true; } /** * Ensure we call one of the outcomes only once, on the right handler. * * Repeated calls will be no-op. */ private static class CallOnceProxy implements OutcomeReceiver<Void, PackageManagerException> { private final Handler mHandler; private final OutcomeReceiver<Void, PackageManagerException> mCallback; @GuardedBy("this") private boolean mCalled = false; CallOnceProxy(Handler handler, OutcomeReceiver<Void, PackageManagerException> callback) { mHandler = handler; mCallback = callback; } @Override public void onResult(Void result) { synchronized (this) { if (!mCalled) { mHandler.post(() -> { mCallback.onResult(null); }); mCalled = true; } } } @Override public void onError(@NonNull PackageManagerException error) { synchronized (this) { if (!mCalled) { mHandler.post(() -> { mCallback.onError(error); }); mCalled = true; } } } } } services/core/java/com/android/server/pm/PackageInstallerService.java +1 −1 Original line number Diff line number Diff line Loading @@ -347,7 +347,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements synchronized (mVerificationPolicyPerUser) { mVerificationPolicyPerUser.put(USER_SYSTEM, DEFAULT_VERIFICATION_POLICY); } mInstallDependencyHelper = new InstallDependencyHelper( mInstallDependencyHelper = new InstallDependencyHelper(mContext, mPm.mInjector.getSharedLibrariesImpl()); LocalServices.getService(SystemServiceManager.class).startService( Loading services/core/java/com/android/server/pm/PackageInstallerSession.java +1 −1 Original line number Diff line number Diff line Loading @@ -3428,8 +3428,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private void resolveLibraryDependenciesIfNeeded() { synchronized (mLock) { // TODO(b/372862145): Callback should be called on a handler passed as parameter mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(mPackageLite, mPm.snapshotComputer(), userId, mHandler, new OutcomeReceiver<>() { @Override Loading services/core/java/com/android/server/pm/SharedLibrariesImpl.java +3 −2 Original line number Diff line number Diff line Loading @@ -1017,10 +1017,11 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable boolean isSdkOrStatic = libraryType.equals(LIBRARY_TYPE_SDK) || libraryType.equals(LIBRARY_TYPE_STATIC); if (isSdkOrStatic && outMissingSharedLibraryInfos != null) { // TODO(b/372862145): Pass the CertDigest too // If Dependency Installation is supported, try that instead of failing. final List<String> libCertDigests = Arrays.asList(requiredCertDigests[i]); SharedLibraryInfo missingLibrary = new SharedLibraryInfo( libName, libVersion, SharedLibraryInfo.TYPE_SDK_PACKAGE libName, libVersion, SharedLibraryInfo.TYPE_SDK_PACKAGE, libCertDigests ); outMissingSharedLibraryInfos.add(missingLibrary); } else { Loading services/tests/mockingservicestests/src/com/android/server/pm/InstallDependencyHelperTest.java +15 −5 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import android.content.Context; import android.content.pm.SharedLibraryInfo; import android.content.pm.parsing.ApkLite; import android.content.pm.parsing.ApkLiteParseUtils; Loading @@ -34,6 +35,8 @@ import android.content.pm.parsing.PackageLite; import android.content.pm.parsing.result.ParseResult; import android.content.pm.parsing.result.ParseTypeImpl; import android.os.FileUtils; import android.os.Handler; import android.os.Looper; import android.os.OutcomeReceiver; import android.platform.test.annotations.Presubmit; import android.platform.test.annotations.RequiresFlagsEnabled; Loading Loading @@ -71,13 +74,17 @@ public class InstallDependencyHelperTest { private static final String PUSH_FILE_DIR = "/data/local/tmp/tests/smockingservicestest/pm/"; private static final String TEST_APP_USING_SDK1_AND_SDK2 = "HelloWorldUsingSdk1And2.apk"; private final Handler mHandler = new Handler(Looper.getMainLooper()); @Mock private SharedLibrariesImpl mSharedLibraries; @Mock private Context mContext; @Mock private Computer mComputer; private InstallDependencyHelper mInstallDependencyHelper; @Before public void setUp() { MockitoAnnotations.initMocks(this); mInstallDependencyHelper = new InstallDependencyHelper(mSharedLibraries); mInstallDependencyHelper = new InstallDependencyHelper(mContext, mSharedLibraries); } @Test Loading @@ -88,7 +95,8 @@ public class InstallDependencyHelperTest { PackageLite pkg = getPackageLite(TEST_APP_USING_SDK1_AND_SDK2); CallbackHelper callback = new CallbackHelper(/*expectSuccess=*/ false); mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(pkg, callback); mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(pkg, mComputer, 0, mHandler, callback); callback.assertFailure(); assertThat(callback.error).hasMessageThat().contains("xyz"); Loading @@ -104,11 +112,12 @@ public class InstallDependencyHelperTest { .thenReturn(missingDependency); CallbackHelper callback = new CallbackHelper(/*expectSuccess=*/ false); mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(pkg, callback); mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(pkg, mComputer, 0, mHandler, callback); callback.assertFailure(); assertThat(callback.error).hasMessageThat().contains( "Failed to bind to Dependency Installer"); "Dependency Installer Service not found"); } Loading @@ -121,7 +130,8 @@ public class InstallDependencyHelperTest { .thenReturn(missingDependency); CallbackHelper callback = new CallbackHelper(/*expectSuccess=*/ true); mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(pkg, callback); mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(pkg, mComputer, 0, mHandler, callback); callback.assertSuccess(); } Loading Loading
services/core/java/com/android/server/pm/InstallDependencyHelper.java +203 −14 Original line number Diff line number Diff line Loading @@ -17,51 +17,240 @@ package com.android.server.pm; import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY; import static android.os.Process.SYSTEM_UID; import android.annotation.NonNull; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ResolveInfo; import android.content.pm.SharedLibraryInfo; import android.content.pm.dependencyinstaller.DependencyInstallerCallback; import android.content.pm.dependencyinstaller.IDependencyInstallerCallback; import android.content.pm.dependencyinstaller.IDependencyInstallerService; import android.content.pm.parsing.PackageLite; import android.os.Handler; import android.os.OutcomeReceiver; import android.os.Process; import android.os.RemoteException; import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.infra.AndroidFuture; import com.android.internal.infra.ServiceConnector; import java.util.List; import java.util.concurrent.TimeUnit; /** * Helper class to interact with SDK Dependency Installer service. */ public class InstallDependencyHelper { private static final String TAG = InstallDependencyHelper.class.getSimpleName(); private static final boolean DEBUG = true; private static final String ACTION_INSTALL_DEPENDENCY = "android.intent.action.INSTALL_DEPENDENCY"; // The maximum amount of time to wait before the system unbinds from the verifier. private static final long UNBIND_TIMEOUT_MILLIS = TimeUnit.HOURS.toMillis(6); private static final long REQUEST_TIMEOUT_MILLIS = TimeUnit.MINUTES.toMillis(1); private final SharedLibrariesImpl mSharedLibraries; private final Context mContext; private final Object mRemoteServiceLock = new Object(); InstallDependencyHelper(SharedLibrariesImpl sharedLibraries) { @GuardedBy("mRemoteServiceLock") private ServiceConnector<IDependencyInstallerService> mRemoteService = null; InstallDependencyHelper(Context context, SharedLibrariesImpl sharedLibraries) { mContext = context; mSharedLibraries = sharedLibraries; } void resolveLibraryDependenciesIfNeeded(PackageLite pkg, OutcomeReceiver<Void, PackageManagerException> callback) { final List<SharedLibraryInfo> missing; void resolveLibraryDependenciesIfNeeded(PackageLite pkg, Computer snapshot, int userId, Handler handler, OutcomeReceiver<Void, PackageManagerException> origCallback) { CallOnceProxy callback = new CallOnceProxy(handler, origCallback); try { missing = mSharedLibraries.collectMissingSharedLibraryInfos(pkg); resolveLibraryDependenciesIfNeededInternal(pkg, snapshot, userId, handler, callback); } catch (PackageManagerException e) { callback.onError(e); return; } catch (Exception e) { onError(callback, e.getMessage()); } } private void resolveLibraryDependenciesIfNeededInternal(PackageLite pkg, Computer snapshot, int userId, Handler handler, CallOnceProxy callback) throws PackageManagerException { final List<SharedLibraryInfo> missing = mSharedLibraries.collectMissingSharedLibraryInfos(pkg); if (missing.isEmpty()) { if (DEBUG) { Slog.i(TAG, "No missing dependency for " + pkg); } // No need for dependency resolution. Move to installation directly. callback.onResult(null); return; } try { bindToDependencyInstaller(); } catch (Exception e) { if (!bindToDependencyInstallerIfNeeded(userId, handler, snapshot)) { onError(callback, "Dependency Installer Service not found"); return; } IDependencyInstallerCallback serviceCallback = new IDependencyInstallerCallback.Stub() { @Override public void onAllDependenciesResolved(int[] sessionIds) throws RemoteException { // TODO(b/372862145): Implement waiting for sessions to finish installation callback.onResult(null); } @Override public void onFailureToResolveAllDependencies() throws RemoteException { onError(callback, "Failed to resolve all dependencies automatically"); } }; boolean scheduleSuccess; synchronized (mRemoteServiceLock) { scheduleSuccess = mRemoteService.run(service -> { service.onDependenciesRequired(missing, new DependencyInstallerCallback(serviceCallback.asBinder())); }); } if (!scheduleSuccess) { onError(callback, "Failed to schedule job on Dependency Installer Service"); } } private void onError(CallOnceProxy callback, String msg) { PackageManagerException pe = new PackageManagerException( INSTALL_FAILED_MISSING_SHARED_LIBRARY, e.getMessage()); INSTALL_FAILED_MISSING_SHARED_LIBRARY, msg); callback.onError(pe); } private boolean bindToDependencyInstallerIfNeeded(int userId, Handler handler, Computer snapshot) { synchronized (mRemoteServiceLock) { if (mRemoteService != null) { if (DEBUG) { Slog.i(TAG, "DependencyInstallerService already bound"); } return true; } } private void bindToDependencyInstaller() { throw new IllegalStateException("Failed to bind to Dependency Installer"); Intent serviceIntent = new Intent(ACTION_INSTALL_DEPENDENCY); // TODO(b/372862145): Use RoleManager to find the package name List<ResolveInfo> resolvedIntents = snapshot.queryIntentServicesInternal( serviceIntent, /*resolvedType=*/ null, /*flags=*/0, userId, SYSTEM_UID, Process.INVALID_PID, /*includeInstantApps*/ false, /*resolveForStart*/ false); if (resolvedIntents.isEmpty()) { return false; } ResolveInfo resolveInfo = resolvedIntents.getFirst(); ComponentName componentName = resolveInfo.getComponentInfo().getComponentName(); serviceIntent.setComponent(componentName); ServiceConnector<IDependencyInstallerService> serviceConnector = new ServiceConnector.Impl<IDependencyInstallerService>(mContext, serviceIntent, Context.BIND_AUTO_CREATE, userId, IDependencyInstallerService.Stub::asInterface) { @Override protected Handler getJobHandler() { return handler; } @Override protected long getRequestTimeoutMs() { return REQUEST_TIMEOUT_MILLIS; } @Override protected long getAutoDisconnectTimeoutMs() { return UNBIND_TIMEOUT_MILLIS; } }; synchronized (mRemoteServiceLock) { // Some other thread managed to connect to the service first if (mRemoteService != null) { return true; } mRemoteService = serviceConnector; mRemoteService.setServiceLifecycleCallbacks( new ServiceConnector.ServiceLifecycleCallbacks<>() { @Override public void onDisconnected(@NonNull IDependencyInstallerService service) { Slog.w(TAG, "DependencyInstallerService " + componentName + " is disconnected"); destroy(); } @Override public void onBinderDied() { Slog.w(TAG, "DependencyInstallerService " + componentName + " has died"); destroy(); } private void destroy() { synchronized (mRemoteServiceLock) { if (mRemoteService != null) { mRemoteService.unbind(); mRemoteService = null; } } } }); AndroidFuture<IDependencyInstallerService> unusedFuture = mRemoteService.connect(); } return true; } /** * Ensure we call one of the outcomes only once, on the right handler. * * Repeated calls will be no-op. */ private static class CallOnceProxy implements OutcomeReceiver<Void, PackageManagerException> { private final Handler mHandler; private final OutcomeReceiver<Void, PackageManagerException> mCallback; @GuardedBy("this") private boolean mCalled = false; CallOnceProxy(Handler handler, OutcomeReceiver<Void, PackageManagerException> callback) { mHandler = handler; mCallback = callback; } @Override public void onResult(Void result) { synchronized (this) { if (!mCalled) { mHandler.post(() -> { mCallback.onResult(null); }); mCalled = true; } } } @Override public void onError(@NonNull PackageManagerException error) { synchronized (this) { if (!mCalled) { mHandler.post(() -> { mCallback.onError(error); }); mCalled = true; } } } } }
services/core/java/com/android/server/pm/PackageInstallerService.java +1 −1 Original line number Diff line number Diff line Loading @@ -347,7 +347,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements synchronized (mVerificationPolicyPerUser) { mVerificationPolicyPerUser.put(USER_SYSTEM, DEFAULT_VERIFICATION_POLICY); } mInstallDependencyHelper = new InstallDependencyHelper( mInstallDependencyHelper = new InstallDependencyHelper(mContext, mPm.mInjector.getSharedLibrariesImpl()); LocalServices.getService(SystemServiceManager.class).startService( Loading
services/core/java/com/android/server/pm/PackageInstallerSession.java +1 −1 Original line number Diff line number Diff line Loading @@ -3428,8 +3428,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private void resolveLibraryDependenciesIfNeeded() { synchronized (mLock) { // TODO(b/372862145): Callback should be called on a handler passed as parameter mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(mPackageLite, mPm.snapshotComputer(), userId, mHandler, new OutcomeReceiver<>() { @Override Loading
services/core/java/com/android/server/pm/SharedLibrariesImpl.java +3 −2 Original line number Diff line number Diff line Loading @@ -1017,10 +1017,11 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable boolean isSdkOrStatic = libraryType.equals(LIBRARY_TYPE_SDK) || libraryType.equals(LIBRARY_TYPE_STATIC); if (isSdkOrStatic && outMissingSharedLibraryInfos != null) { // TODO(b/372862145): Pass the CertDigest too // If Dependency Installation is supported, try that instead of failing. final List<String> libCertDigests = Arrays.asList(requiredCertDigests[i]); SharedLibraryInfo missingLibrary = new SharedLibraryInfo( libName, libVersion, SharedLibraryInfo.TYPE_SDK_PACKAGE libName, libVersion, SharedLibraryInfo.TYPE_SDK_PACKAGE, libCertDigests ); outMissingSharedLibraryInfos.add(missingLibrary); } else { Loading
services/tests/mockingservicestests/src/com/android/server/pm/InstallDependencyHelperTest.java +15 −5 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import android.content.Context; import android.content.pm.SharedLibraryInfo; import android.content.pm.parsing.ApkLite; import android.content.pm.parsing.ApkLiteParseUtils; Loading @@ -34,6 +35,8 @@ import android.content.pm.parsing.PackageLite; import android.content.pm.parsing.result.ParseResult; import android.content.pm.parsing.result.ParseTypeImpl; import android.os.FileUtils; import android.os.Handler; import android.os.Looper; import android.os.OutcomeReceiver; import android.platform.test.annotations.Presubmit; import android.platform.test.annotations.RequiresFlagsEnabled; Loading Loading @@ -71,13 +74,17 @@ public class InstallDependencyHelperTest { private static final String PUSH_FILE_DIR = "/data/local/tmp/tests/smockingservicestest/pm/"; private static final String TEST_APP_USING_SDK1_AND_SDK2 = "HelloWorldUsingSdk1And2.apk"; private final Handler mHandler = new Handler(Looper.getMainLooper()); @Mock private SharedLibrariesImpl mSharedLibraries; @Mock private Context mContext; @Mock private Computer mComputer; private InstallDependencyHelper mInstallDependencyHelper; @Before public void setUp() { MockitoAnnotations.initMocks(this); mInstallDependencyHelper = new InstallDependencyHelper(mSharedLibraries); mInstallDependencyHelper = new InstallDependencyHelper(mContext, mSharedLibraries); } @Test Loading @@ -88,7 +95,8 @@ public class InstallDependencyHelperTest { PackageLite pkg = getPackageLite(TEST_APP_USING_SDK1_AND_SDK2); CallbackHelper callback = new CallbackHelper(/*expectSuccess=*/ false); mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(pkg, callback); mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(pkg, mComputer, 0, mHandler, callback); callback.assertFailure(); assertThat(callback.error).hasMessageThat().contains("xyz"); Loading @@ -104,11 +112,12 @@ public class InstallDependencyHelperTest { .thenReturn(missingDependency); CallbackHelper callback = new CallbackHelper(/*expectSuccess=*/ false); mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(pkg, callback); mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(pkg, mComputer, 0, mHandler, callback); callback.assertFailure(); assertThat(callback.error).hasMessageThat().contains( "Failed to bind to Dependency Installer"); "Dependency Installer Service not found"); } Loading @@ -121,7 +130,8 @@ public class InstallDependencyHelperTest { .thenReturn(missingDependency); CallbackHelper callback = new CallbackHelper(/*expectSuccess=*/ true); mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(pkg, callback); mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(pkg, mComputer, 0, mHandler, callback); callback.assertSuccess(); } Loading