Support callbacks for results of optimizing package(s).

Bug: 257027956
Test: ArtServiceTests
Ignore-AOSP-First: ART Services.
Change-Id: I2ac70058ef5678641216cc37f68993981157c064
diff --git a/libartservice/service/javatests/com/android/server/art/DexOptHelperTest.java b/libartservice/service/javatests/com/android/server/art/DexOptHelperTest.java
index f71f46d..e12cd5d 100644
--- a/libartservice/service/javatests/com/android/server/art/DexOptHelperTest.java
+++ b/libartservice/service/javatests/com/android/server/art/DexOptHelperTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server.art;
 
+import static com.android.server.art.ArtManagerLocal.OptimizePackageDoneCallback;
 import static com.android.server.art.model.OptimizeResult.DexContainerFileOptimizeResult;
 import static com.android.server.art.model.OptimizeResult.PackageOptimizeResult;
 
@@ -41,6 +42,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.server.art.model.ArtFlags;
+import com.android.server.art.model.Config;
 import com.android.server.art.model.OptimizeParams;
 import com.android.server.art.model.OptimizeResult;
 import com.android.server.pm.PackageManagerLocal;
@@ -56,6 +58,7 @@
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnitRunner;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -95,15 +98,13 @@
     private ExecutorService mExecutor = Executors.newSingleThreadExecutor();
     private List<DexContainerFileOptimizeResult> mPrimaryResults;
     private List<DexContainerFileOptimizeResult> mSecondaryResults;
+    private Config mConfig;
     private OptimizeParams mParams;
     private List<String> mRequestedPackages;
     private DexOptHelper mDexOptHelper;
 
     @Before
     public void setUp() throws Exception {
-        lenient().when(mInjector.getAppHibernationManager()).thenReturn(mAhm);
-        lenient().when(mInjector.getPowerManager()).thenReturn(mPowerManager);
-
         lenient()
                 .when(mPowerManager.newWakeLock(eq(PowerManager.PARTIAL_WAKE_LOCK), any()))
                 .thenReturn(mWakeLock);
@@ -112,6 +113,7 @@
         lenient().when(mAhm.isOatArtifactDeletionEnabled()).thenReturn(true);
 
         mCancellationSignal = new CancellationSignal();
+        mConfig = new Config();
 
         preparePackagesAndLibraries();
 
@@ -137,6 +139,10 @@
                                           | ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES)
                           .build();
 
+        lenient().when(mInjector.getAppHibernationManager()).thenReturn(mAhm);
+        lenient().when(mInjector.getPowerManager()).thenReturn(mPowerManager);
+        lenient().when(mInjector.getConfig()).thenReturn(mConfig);
+
         mDexOptHelper = new DexOptHelper(mInjector);
     }
 
@@ -420,6 +426,47 @@
         verifyNoDexopt();
     }
 
+    @Test
+    public void testCallbacks() throws Exception {
+        List<OptimizeResult> list1 = new ArrayList<>();
+        mConfig.addOptimizePackageDoneCallback(Runnable::run, result -> list1.add(result));
+
+        List<OptimizeResult> list2 = new ArrayList<>();
+        mConfig.addOptimizePackageDoneCallback(Runnable::run, result -> list2.add(result));
+
+        OptimizeResult result = mDexOptHelper.dexopt(
+                mSnapshot, mRequestedPackages, mParams, mCancellationSignal, mExecutor);
+
+        assertThat(list1).containsExactly(result);
+        assertThat(list2).containsExactly(result);
+    }
+
+    @Test
+    public void testCallbackRemoved() throws Exception {
+        List<OptimizeResult> list1 = new ArrayList<>();
+        OptimizePackageDoneCallback callback1 = result -> list1.add(result);
+        mConfig.addOptimizePackageDoneCallback(Runnable::run, callback1);
+
+        List<OptimizeResult> list2 = new ArrayList<>();
+        mConfig.addOptimizePackageDoneCallback(Runnable::run, result -> list2.add(result));
+
+        mConfig.removeOptimizePackageDoneCallback(callback1);
+
+        OptimizeResult result = mDexOptHelper.dexopt(
+                mSnapshot, mRequestedPackages, mParams, mCancellationSignal, mExecutor);
+
+        assertThat(list1).isEmpty();
+        assertThat(list2).containsExactly(result);
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testCallbackAlreadyAdded() throws Exception {
+        List<OptimizeResult> list = new ArrayList<>();
+        OptimizePackageDoneCallback callback = result -> list.add(result);
+        mConfig.addOptimizePackageDoneCallback(Runnable::run, callback);
+        mConfig.addOptimizePackageDoneCallback(Runnable::run, callback);
+    }
+
     private AndroidPackage createPackage() {
         AndroidPackage pkg = mock(AndroidPackage.class);
         var baseSplit = mock(AndroidPackageSplit.class);