1 /* <lambda>null2 * Copyright (C) 2019 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 package com.android.server.pm 17 18 import android.content.Context 19 import android.content.pm.PackageInstaller 20 import android.content.pm.PackageInstaller.SessionParams 21 import android.content.pm.PackageInstaller.SessionParams.PERMISSION_STATE_DEFAULT 22 import android.content.pm.PackageInstaller.SessionParams.PERMISSION_STATE_DENIED 23 import android.content.pm.PackageInstaller.SessionParams.PERMISSION_STATE_GRANTED 24 import android.content.pm.PackageManager 25 import android.content.pm.verify.domain.DomainSet 26 import android.os.Parcel 27 import android.os.Process 28 import android.platform.test.annotations.Presubmit 29 import android.util.AtomicFile 30 import android.util.Slog 31 import android.util.Xml 32 import com.android.internal.os.BackgroundThread 33 import com.android.server.testutils.whenever 34 import com.google.common.truth.Truth.assertThat 35 import libcore.io.IoUtils 36 import org.junit.Before 37 import org.junit.Rule 38 import org.junit.Test 39 import org.junit.rules.TemporaryFolder 40 import org.mockito.ArgumentMatchers.anyInt 41 import org.mockito.ArgumentMatchers.anyLong 42 import org.mockito.ArgumentMatchers.anyString 43 import org.mockito.Mock 44 import org.mockito.Mockito.mock 45 import org.mockito.MockitoAnnotations 46 import org.xmlpull.v1.XmlPullParser 47 import org.xmlpull.v1.XmlPullParserException 48 import java.io.File 49 import java.io.FileInputStream 50 import java.io.FileNotFoundException 51 import java.io.FileOutputStream 52 import java.io.IOException 53 54 @Presubmit 55 class PackageInstallerSessionTest { 56 57 companion object { 58 private const val TAG_SESSIONS = "sessions" 59 } 60 61 @JvmField 62 @Rule 63 var mTemporaryFolder = TemporaryFolder() 64 65 private lateinit var mTmpDir: File 66 private lateinit var mSessionsFile: AtomicFile 67 68 @Mock 69 lateinit var mMockPackageManagerInternal: PackageManagerService 70 71 @Mock 72 lateinit var mSnapshot: Computer 73 74 @Before 75 @Throws(Exception::class) 76 fun setUp() { 77 mTmpDir = mTemporaryFolder.newFolder("PackageInstallerSessionTest") 78 mSessionsFile = AtomicFile( 79 File(mTmpDir.getAbsolutePath() + "/sessions.xml"), "package-session" 80 ) 81 MockitoAnnotations.initMocks(this) 82 whenever(mSnapshot.getPackageUid(anyString(), anyLong(), anyInt())) { 0 } 83 whenever(mMockPackageManagerInternal.snapshotComputer()) { mSnapshot } 84 } 85 86 @Test 87 fun testWriteAndRestoreSessionXmlSimpleSession() { 88 writeRestoreAssert(listOf(createSession())) 89 } 90 91 @Test 92 fun testWriteAndRestoreSessionXmlStagedSession() { 93 writeRestoreAssert(listOf(createSession(staged = true))) 94 } 95 96 @Test 97 fun testWriteAndRestoreSessionXmlLegacyGrantedPermission() { 98 val sessions = createSession { 99 @Suppress("DEPRECATION") 100 it.setGrantedRuntimePermissions(arrayOf("permission1", "permission2")) 101 }.let(::listOf) 102 103 val restored = writeRestoreAssert(sessions) 104 assertThat(restored.single().params.legacyGrantedRuntimePermissions).asList() 105 .containsExactly("permission1", "permission2") 106 } 107 108 @Test 109 fun testWriteAndRestoreSessionXmlPermissionState() { 110 val sessions = createSession { 111 it.setPermissionState("grantPermission", PERMISSION_STATE_GRANTED) 112 .setPermissionState("denyPermission", PERMISSION_STATE_DENIED) 113 .setPermissionState("grantToDefaultPermission", PERMISSION_STATE_GRANTED) 114 .setPermissionState("grantToDefaultPermission", PERMISSION_STATE_DEFAULT) 115 .setPermissionState("denyToDefaultPermission", PERMISSION_STATE_DENIED) 116 .setPermissionState("denyToDefaultPermission", PERMISSION_STATE_DEFAULT) 117 .setPermissionState("grantToDenyPermission", PERMISSION_STATE_GRANTED) 118 .setPermissionState("grantToDenyPermission", PERMISSION_STATE_DENIED) 119 .setPermissionState("denyToGrantPermission", PERMISSION_STATE_DENIED) 120 .setPermissionState("denyToGrantPermission", PERMISSION_STATE_GRANTED) 121 }.let(::listOf) 122 123 writeRestoreAssert(sessions).single().params.run { 124 assertThat(legacyGrantedRuntimePermissions).asList() 125 .containsExactly("grantPermission", "denyToGrantPermission") 126 assertThat(permissionStates) 127 .containsExactlyEntriesIn(mapOf( 128 "grantPermission" to PERMISSION_STATE_GRANTED, 129 "denyToGrantPermission" to PERMISSION_STATE_GRANTED, 130 "denyPermission" to PERMISSION_STATE_DENIED, 131 "grantToDenyPermission" to PERMISSION_STATE_DENIED, 132 )) 133 } 134 } 135 136 @Test 137 fun testWriteAndRestoreSessionXmlMultiPackageSessions() { 138 val session = createSession( 139 sessionId = 123, 140 multiPackage = true, 141 childSessionIds = listOf(234, 345) 142 ) 143 val childSession1 = createSession(sessionId = 234, parentSessionId = 123) 144 val childSession2 = createSession(sessionId = 345, parentSessionId = 123) 145 writeRestoreAssert(listOf(session, childSession1, childSession2)) 146 } 147 148 private fun createSession( 149 staged: Boolean = false, 150 sessionId: Int = 123, 151 multiPackage: Boolean = false, 152 parentSessionId: Int = PackageInstaller.SessionInfo.INVALID_ID, 153 childSessionIds: List<Int> = emptyList(), 154 block: (SessionParams) -> Unit = {}, 155 ): PackageInstallerSession { 156 val params = SessionParams(SessionParams.MODE_FULL_INSTALL).apply { 157 isStaged = staged 158 isMultiPackage = multiPackage 159 block(this) 160 } 161 162 val installSource = InstallSource.create( 163 "testInstallInitiator", 164 "testInstallOriginator", "testInstaller", -1, "testUpdateOwner", 165 "testAttributionTag", PackageInstaller.PACKAGE_SOURCE_UNSPECIFIED 166 ) 167 168 return PackageInstallerSession( 169 /* callback */ null, 170 /* context */ null, 171 /* pm */ mMockPackageManagerInternal, 172 /* sessionProvider */ null, 173 /* silentUpdatePolicy */ null, 174 /* looper */ BackgroundThread.getHandler().looper, 175 /* stagingManager */ null, 176 /* sessionId */ sessionId, 177 /* userId */ 456, 178 /* installerUid */ Process.myUid(), 179 /* installSource */ installSource, 180 /* sessionParams */ params, 181 /* createdMillis */ 0L, 182 /* committedMillis */ 0L, 183 /* stageDir */ mTmpDir, 184 /* stageCid */ null, 185 /* files */ null, 186 /* checksums */ null, 187 /* prepared */ true, 188 /* committed */ false, 189 /* destroyed */ false, 190 /* sealed */ false, // Setting to true would trigger some PM logic. 191 /* childSessionIds */ childSessionIds.toIntArray(), 192 /* parentSessionId */ parentSessionId, 193 /* isReady */ staged, 194 /* isFailed */ false, 195 /* isApplied */ false, 196 /* stagedSessionErrorCode */ PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE, 197 /* stagedSessionErrorMessage */ "some error", 198 /* preVerifiedDomains */ DomainSet(setOf("com.foo", "com.bar")), 199 /* installDependencyHelper */ null 200 ) 201 } 202 203 private fun writeRestoreAssert(sessions: List<PackageInstallerSession>) = 204 writeSessions(sessions) 205 .run { restoreSessions() } 206 .also { assertEquals(sessions, it) } 207 208 private fun writeSessions(sessions: List<PackageInstallerSession>) { 209 var fos: FileOutputStream? = null 210 try { 211 fos = mSessionsFile.startWrite() 212 Xml.resolveSerializer(fos).apply { 213 startDocument(null, true) 214 startTag(null, TAG_SESSIONS) 215 for (session in sessions) { 216 session.write(this, mTmpDir) 217 } 218 endTag(null, TAG_SESSIONS) 219 endDocument() 220 } 221 mSessionsFile.finishWrite(fos) 222 Slog.d("PackageInstallerSessionTest", String(mSessionsFile.readFully())) 223 } catch (e: IOException) { 224 mSessionsFile.failWrite(fos) 225 } 226 } 227 228 // This is roughly the logic used in PackageInstallerService to read the session. Note that 229 // this test stresses readFromXml method from PackageInstallerSession, and doesn't cover the 230 // PackageInstallerService portion of the parsing. 231 private fun restoreSessions(): List<PackageInstallerSession> { 232 val ret: MutableList<PackageInstallerSession> = ArrayList() 233 var fis: FileInputStream? = null 234 try { 235 fis = mSessionsFile.openRead() 236 val parser = Xml.resolvePullParser(fis) 237 var type: Int 238 while (parser.next().also { type = it } != XmlPullParser.END_DOCUMENT) { 239 if (type == XmlPullParser.START_TAG) { 240 val tag = parser.name 241 if (PackageInstallerSession.TAG_SESSION == tag) { 242 val session: PackageInstallerSession 243 try { 244 session = PackageInstallerSession.readFromXml( 245 parser, 246 mock(PackageInstallerService.InternalCallback::class.java), 247 mock(Context::class.java), 248 mMockPackageManagerInternal, 249 BackgroundThread.getHandler().looper, 250 mock(StagingManager::class.java), 251 mTmpDir, 252 mock(PackageSessionProvider::class.java), 253 mock(SilentUpdatePolicy::class.java), 254 mock(InstallDependencyHelper::class.java) 255 ) 256 ret.add(session) 257 } catch (e: Exception) { 258 Slog.e("PackageInstallerSessionTest", "Exception ", e) 259 continue 260 } 261 } 262 } 263 } 264 } catch (_: FileNotFoundException) { 265 // Missing sessions are okay, probably first boot 266 } catch (_: IOException) { 267 } catch (_: XmlPullParserException) { 268 } finally { 269 IoUtils.closeQuietly(fis) 270 } 271 return ret 272 } 273 274 private fun assertSessionParamsEquivalent(expected: SessionParams, actual: SessionParams) { 275 assertThat(expected.mode).isEqualTo(actual.mode) 276 assertThat(expected.installFlags).isEqualTo(actual.installFlags) 277 assertThat(expected.installLocation).isEqualTo(actual.installLocation) 278 assertThat(expected.installReason).isEqualTo(actual.installReason) 279 assertThat(expected.sizeBytes).isEqualTo(actual.sizeBytes) 280 assertThat(expected.appPackageName).isEqualTo(actual.appPackageName) 281 assertThat(expected.appIcon).isEqualTo(actual.appIcon) 282 assertThat(expected.originatingUri).isEqualTo(actual.originatingUri) 283 assertThat(expected.originatingUid).isEqualTo(actual.originatingUid) 284 assertThat(expected.referrerUri).isEqualTo(actual.referrerUri) 285 assertThat(expected.abiOverride).isEqualTo(actual.abiOverride) 286 assertThat(expected.volumeUuid).isEqualTo(actual.volumeUuid) 287 assertThat(expected.permissionStates).isEqualTo(actual.permissionStates) 288 assertThat(expected.installerPackageName).isEqualTo(actual.installerPackageName) 289 assertThat(expected.isMultiPackage).isEqualTo(actual.isMultiPackage) 290 assertThat(expected.isStaged).isEqualTo(actual.isStaged) 291 } 292 293 private fun assertEquals( 294 expected: List<PackageInstallerSession>, 295 actual: List<PackageInstallerSession> 296 ) { 297 assertThat(expected).hasSize(actual.size) 298 expected.sortedBy { it.sessionId }.zip(actual.sortedBy { it.sessionId }) 299 .forEach { (expected, actual) -> 300 assertEquals(expected, actual) 301 } 302 } 303 304 private fun assertEquals(expected: PackageInstallerSession, actual: PackageInstallerSession) { 305 // Check both the restored params and an unparcelized variant to ensure parcelling works 306 assertSessionParamsEquivalent(expected.params, actual.params) 307 assertSessionParamsEquivalent(expected.params, actual.params.let { 308 val parcel = Parcel.obtain() 309 it.writeToParcel(parcel, 0) 310 parcel.setDataPosition(0) 311 SessionParams.CREATOR.createFromParcel(parcel).also { 312 parcel.recycle() 313 } 314 }) 315 316 assertThat(expected.sessionId).isEqualTo(actual.sessionId) 317 assertThat(expected.userId).isEqualTo(actual.userId) 318 assertThat(expected.installerUid).isEqualTo(actual.installerUid) 319 assertThat(expected.installerPackageName).isEqualTo(actual.installerPackageName) 320 assertInstallSourcesEquivalent(expected.installSource, actual.installSource) 321 assertThat(expected.stageDir.absolutePath).isEqualTo(actual.stageDir.absolutePath) 322 assertThat(expected.stageCid).isEqualTo(actual.stageCid) 323 assertThat(expected.isPrepared).isEqualTo(actual.isPrepared) 324 assertThat(expected.isStaged).isEqualTo(actual.isStaged) 325 assertThat(expected.isSessionApplied).isEqualTo(actual.isSessionApplied) 326 assertThat(expected.isSessionFailed).isEqualTo(actual.isSessionFailed) 327 assertThat(expected.isSessionReady).isEqualTo(actual.isSessionReady) 328 assertThat(expected.sessionErrorCode).isEqualTo(actual.sessionErrorCode) 329 assertThat(expected.sessionErrorMessage).isEqualTo(actual.sessionErrorMessage) 330 assertThat(expected.isPrepared).isEqualTo(actual.isPrepared) 331 assertThat(expected.isCommitted).isEqualTo(actual.isCommitted) 332 assertThat(expected.isPreapprovalRequested).isEqualTo(actual.isPreapprovalRequested) 333 assertThat(expected.createdMillis).isEqualTo(actual.createdMillis) 334 assertThat(expected.isSealed).isEqualTo(actual.isSealed) 335 assertThat(expected.isMultiPackage).isEqualTo(actual.isMultiPackage) 336 assertThat(expected.hasParentSessionId()).isEqualTo(actual.hasParentSessionId()) 337 assertThat(expected.parentSessionId).isEqualTo(actual.parentSessionId) 338 assertThat(expected.childSessionIds).asList() 339 .containsExactlyElementsIn(actual.childSessionIds.toList()) 340 assertThat(expected.preVerifiedDomains).isEqualTo(actual.preVerifiedDomains) 341 } 342 343 private fun assertInstallSourcesEquivalent(expected: InstallSource, actual: InstallSource) { 344 assertThat(expected.mInstallerPackageName).isEqualTo(actual.mInstallerPackageName) 345 assertThat(expected.mInitiatingPackageName).isEqualTo(actual.mInitiatingPackageName) 346 assertThat(expected.mOriginatingPackageName).isEqualTo(actual.mOriginatingPackageName) 347 } 348 } 349