1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.content.pm; 18 19 import android.Manifest; 20 import android.app.UiAutomation; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.IntentSender; 24 import android.os.HandlerThread; 25 import android.os.ParcelFileDescriptor; 26 import android.perftests.utils.BenchmarkState; 27 import android.perftests.utils.PerfStatusReporter; 28 import android.util.Log; 29 30 import androidx.annotation.NonNull; 31 import androidx.test.platform.app.InstrumentationRegistry; 32 33 import com.android.compatibility.common.util.AdoptShellPermissionsRule; 34 import com.android.cts.install.lib.Install; 35 import com.android.cts.install.lib.InstallUtils; 36 import com.android.cts.install.lib.LocalIntentSender; 37 import com.android.cts.install.lib.TestApp; 38 39 import org.junit.After; 40 import org.junit.Before; 41 import org.junit.Rule; 42 import org.junit.Test; 43 44 import java.io.ByteArrayOutputStream; 45 import java.io.IOException; 46 import java.io.InputStream; 47 import java.io.OutputStream; 48 import java.util.ArrayList; 49 import java.util.List; 50 import java.util.concurrent.CountDownLatch; 51 import java.util.concurrent.TimeUnit; 52 53 public class PackageInstallerBenchmark { 54 private static final String TAG = "PackageInstallerBenchmark"; 55 56 @Rule 57 public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); 58 59 /** 60 * This rule adopts the Shell process permissions, needed because INSTALL_PACKAGES 61 * and DELETE_PACKAGES are privileged permission. 62 */ 63 @Rule 64 public AdoptShellPermissionsRule mAdoptShellPermissionsRule = new AdoptShellPermissionsRule( 65 InstrumentationRegistry.getInstrumentation().getUiAutomation(), 66 Manifest.permission.INSTALL_PACKAGES, 67 Manifest.permission.DELETE_PACKAGES); 68 69 private static class SessionCallback extends PackageInstaller.SessionCallback { 70 private final List<Integer> mExpectedSessions; 71 private final CountDownLatch mCountDownLatch; 72 private final boolean mExpectedSuccess; 73 SessionCallback(boolean expectedSuccess, List<Integer> expectedSessions, @NonNull CountDownLatch countDownLatch)74 SessionCallback(boolean expectedSuccess, List<Integer> expectedSessions, 75 @NonNull CountDownLatch countDownLatch) { 76 mExpectedSuccess = expectedSuccess; 77 mCountDownLatch = countDownLatch; 78 mExpectedSessions = expectedSessions; 79 } 80 81 @Override onCreated(int sessionId)82 public void onCreated(int sessionId) { } 83 84 @Override onBadgingChanged(int sessionId)85 public void onBadgingChanged(int sessionId) { } 86 87 @Override onActiveChanged(int sessionId, boolean active)88 public void onActiveChanged(int sessionId, boolean active) { } 89 90 @Override onProgressChanged(int sessionId, float progress)91 public void onProgressChanged(int sessionId, float progress) { } 92 93 @Override onFinished(int sessionId, boolean success)94 public void onFinished(int sessionId, boolean success) { 95 if (success == mExpectedSuccess && mExpectedSessions.contains(sessionId)) { 96 mCountDownLatch.countDown(); 97 } 98 } 99 } 100 101 private CountDownLatch mCountDownLatch; 102 private SessionCallback mSessionCallback; 103 private PackageInstaller mPackageInstaller; 104 private Install mInstall; 105 private HandlerThread mHandlerThread; 106 private List<PackageInstaller.Session> mExpectedSessions; 107 private List<Integer> mExpectedSessionIds; 108 final LocalIntentSender mLocalIntentSender = new LocalIntentSender(); 109 private IntentSender mIntentSender; 110 111 @Before setUp()112 public void setUp() throws IOException { 113 final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); 114 mPackageInstaller = context.getPackageManager().getPackageInstaller(); 115 mHandlerThread = new HandlerThread("PackageInstallerBenchmark"); 116 mHandlerThread.start(); 117 118 mIntentSender = mLocalIntentSender.getIntentSender(); 119 } 120 121 @After tearDown()122 public void tearDown() throws InterruptedException { 123 final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); 124 context.unregisterReceiver(mLocalIntentSender); 125 126 uninstall(false /* stop at fail */, TestApp.A, TestApp.B, TestApp.C); 127 mHandlerThread.quitSafely(); 128 } 129 createSinglePackageSessions( BenchmarkState state, boolean expectedResult, TestApp...testApps)130 private List<PackageInstaller.Session> createSinglePackageSessions( 131 BenchmarkState state, boolean expectedResult, TestApp...testApps) 132 throws IOException, InterruptedException { 133 state.pauseTiming(); 134 uninstall(false /* stop at fail */, testApps); 135 136 mExpectedSessions = new ArrayList<>(); 137 mExpectedSessionIds = new ArrayList<>(); 138 for (TestApp testApp : testApps) { 139 mInstall = Install.single(testApp); 140 final int expectedSessionId = mInstall.createSession(); 141 PackageInstaller.Session session = 142 InstallUtils.openPackageInstallerSession(expectedSessionId); 143 Log.d(TAG, "createNewSession: session expectedSessionId = " + expectedSessionId); 144 mExpectedSessions.add(session); 145 mExpectedSessionIds.add(expectedSessionId); 146 } 147 148 mCountDownLatch = new CountDownLatch(mExpectedSessions.size()); 149 mSessionCallback = new SessionCallback(expectedResult, mExpectedSessionIds, 150 mCountDownLatch); 151 mPackageInstaller.registerSessionCallback(mSessionCallback, 152 mHandlerThread.getThreadHandler()); 153 state.resumeTiming(); 154 return mExpectedSessions; 155 } 156 createMultiplePackageSessions(BenchmarkState state, boolean expectedSuccess, List<TestApp[]> testAppsList)157 private List<PackageInstaller.Session> createMultiplePackageSessions(BenchmarkState state, 158 boolean expectedSuccess, List<TestApp[]> testAppsList) 159 throws IOException, InterruptedException { 160 state.pauseTiming(); 161 mExpectedSessions = new ArrayList<>(); 162 mExpectedSessionIds = new ArrayList<>(); 163 for (TestApp[] testApps : testAppsList) { 164 uninstall(false /* stop at fail */, testApps); 165 166 mInstall = Install.multi(testApps); 167 final int expectedSessionId = mInstall.createSession(); 168 PackageInstaller.Session session = 169 InstallUtils.openPackageInstallerSession(expectedSessionId); 170 mExpectedSessions.add(session); 171 mExpectedSessionIds.add(expectedSessionId); 172 } 173 174 mCountDownLatch = new CountDownLatch(mExpectedSessions.size()); 175 mSessionCallback = new SessionCallback(expectedSuccess, mExpectedSessionIds, 176 mCountDownLatch); 177 mPackageInstaller.registerSessionCallback(mSessionCallback, 178 mHandlerThread.getThreadHandler()); 179 state.resumeTiming(); 180 return mExpectedSessions; 181 } 182 uninstall(boolean stopAtFail, TestApp...testApps)183 private void uninstall(boolean stopAtFail, TestApp...testApps) throws InterruptedException { 184 String[] packageNames = new String[testApps.length]; 185 for (int i = 0; i < testApps.length; i++) { 186 packageNames[i] = testApps[i].getPackageName(); 187 } 188 uninstall(stopAtFail, packageNames); 189 } 190 uninstall(boolean stopAtFail, String...packageNames)191 private void uninstall(boolean stopAtFail, String...packageNames) throws InterruptedException { 192 LocalIntentSender localIntentSender = new LocalIntentSender(); 193 IntentSender intentSender = localIntentSender.getIntentSender(); 194 for (String packageName : packageNames) { 195 try { 196 mPackageInstaller.uninstall(packageName, intentSender); 197 } catch (IllegalArgumentException e) { 198 continue; 199 } 200 Intent intent = localIntentSender.getResult(); 201 if (stopAtFail) { 202 InstallUtils.assertStatusSuccess(intent); 203 } 204 } 205 206 final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); 207 context.unregisterReceiver(localIntentSender); 208 } 209 uninstallSession(BenchmarkState state, String...packageNames)210 private void uninstallSession(BenchmarkState state, String...packageNames) 211 throws Exception { 212 state.pauseTiming(); 213 uninstall(true /* stop at fail */, packageNames); 214 mPackageInstaller.unregisterSessionCallback(mSessionCallback); 215 executeShellCommand("pm gc"); 216 state.resumeTiming(); 217 } 218 executeShellCommand(String command)219 private static String executeShellCommand(String command) throws IOException { 220 UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation(); 221 final ParcelFileDescriptor stdout = uiAutomation.executeShellCommand(command); 222 try (InputStream inputStream = new ParcelFileDescriptor.AutoCloseInputStream(stdout); 223 ByteArrayOutputStream result = new ByteArrayOutputStream()) { 224 writeFullStream(inputStream, result); 225 return result.toString("UTF-8"); 226 } 227 } 228 writeFullStream(InputStream inputStream, OutputStream outputStream)229 private static void writeFullStream(InputStream inputStream, OutputStream outputStream) 230 throws IOException { 231 final byte[] buffer = new byte[1024]; 232 int length; 233 while ((length = inputStream.read(buffer)) != -1) { 234 outputStream.write(buffer, 0, length); 235 } 236 } 237 238 @Test(timeout = 600_000L) commit_aSingleApkSession_untilFinishBenchmark()239 public void commit_aSingleApkSession_untilFinishBenchmark() throws Exception { 240 uninstall(false /* stop at fail */, TestApp.A); 241 242 final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); 243 while (state.keepRunning()) { 244 List<PackageInstaller.Session> sessions = 245 createSinglePackageSessions(state, true, TestApp.A1); 246 247 for (PackageInstaller.Session session : sessions) { 248 session.commit(mIntentSender); 249 } 250 mCountDownLatch.await(1, TimeUnit.MINUTES); 251 252 uninstallSession(state, TestApp.A); 253 } 254 } 255 256 @Test(timeout = 600_000L) commit_threeSingleApkSessions_untilFinishBenchmark()257 public void commit_threeSingleApkSessions_untilFinishBenchmark() throws Exception { 258 uninstall(false /* stop at fail */, TestApp.A, TestApp.B, TestApp.C); 259 260 final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); 261 while (state.keepRunning()) { 262 List<PackageInstaller.Session> sessions = createSinglePackageSessions( 263 state, true, TestApp.A1, TestApp.B1, TestApp.C1); 264 265 for (PackageInstaller.Session session : sessions) { 266 session.commit(mIntentSender); 267 } 268 mCountDownLatch.await(1, TimeUnit.MINUTES); 269 270 uninstallSession(state, TestApp.A, TestApp.B, TestApp.C); 271 } 272 } 273 274 @Test(timeout = 600_000L) commit_aMultiplePackagesSession_untilFinishBenchmark()275 public void commit_aMultiplePackagesSession_untilFinishBenchmark() throws Exception { 276 uninstall(false /* stop at fail */, TestApp.A, TestApp.B, TestApp.C); 277 278 final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); 279 final List<TestApp[]> multiPackageApps = new ArrayList<>(); 280 multiPackageApps.add(new TestApp[] {TestApp.A1, TestApp.B1, TestApp.C1}); 281 282 while (state.keepRunning()) { 283 List<PackageInstaller.Session> sessions = createMultiplePackageSessions( 284 state, true, multiPackageApps); 285 286 for (PackageInstaller.Session session : sessions) { 287 session.commit(mIntentSender); 288 } 289 mCountDownLatch.await(1, TimeUnit.MINUTES); 290 291 uninstallSession(state, TestApp.A, TestApp.B, TestApp.C); 292 } 293 } 294 295 @Test(timeout = 600_000L) commit_threeMultiplePackageSessions_untilFinishBenchmark()296 public void commit_threeMultiplePackageSessions_untilFinishBenchmark() throws Exception { 297 uninstall(false /* stop at fail */, TestApp.A, TestApp.B, TestApp.C); 298 299 final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); 300 final List<TestApp[]> multiPackageApps = new ArrayList<>(); 301 multiPackageApps.add(new TestApp[] {TestApp.A1}); 302 multiPackageApps.add(new TestApp[] {TestApp.B1}); 303 multiPackageApps.add(new TestApp[] {TestApp.C1}); 304 305 while (state.keepRunning()) { 306 List<PackageInstaller.Session> sessions = createMultiplePackageSessions( 307 state, true, multiPackageApps); 308 309 for (PackageInstaller.Session session : sessions) { 310 session.commit(mIntentSender); 311 } 312 mCountDownLatch.await(1, TimeUnit.MINUTES); 313 314 uninstallSession(state, TestApp.A, TestApp.B, TestApp.C); 315 } 316 } 317 318 @Test(timeout = 600_000L) commit_aMultipleApksSession_untilFinishBenchmark()319 public void commit_aMultipleApksSession_untilFinishBenchmark() throws Exception { 320 uninstall(false /* stop at fail */, TestApp.A); 321 322 final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); 323 while (state.keepRunning()) { 324 List<PackageInstaller.Session> sessions = createSinglePackageSessions( 325 state, true, TestApp.ASplit1); 326 327 for (PackageInstaller.Session session : sessions) { 328 session.commit(mIntentSender); 329 } 330 mCountDownLatch.await(1, TimeUnit.MINUTES); 331 332 uninstallSession(state, TestApp.A); 333 } 334 } 335 } 336