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 ) 200 } 201 202 private fun writeRestoreAssert(sessions: List<PackageInstallerSession>) = 203 writeSessions(sessions) 204 .run { restoreSessions() } 205 .also { assertEquals(sessions, it) } 206 207 private fun writeSessions(sessions: List<PackageInstallerSession>) { 208 var fos: FileOutputStream? = null 209 try { 210 fos = mSessionsFile.startWrite() 211 Xml.resolveSerializer(fos).apply { 212 startDocument(null, true) 213 startTag(null, TAG_SESSIONS) 214 for (session in sessions) { 215 session.write(this, mTmpDir) 216 } 217 endTag(null, TAG_SESSIONS) 218 endDocument() 219 } 220 mSessionsFile.finishWrite(fos) 221 Slog.d("PackageInstallerSessionTest", String(mSessionsFile.readFully())) 222 } catch (e: IOException) { 223 mSessionsFile.failWrite(fos) 224 } 225 } 226 227 // This is roughly the logic used in PackageInstallerService to read the session. Note that 228 // this test stresses readFromXml method from PackageInstallerSession, and doesn't cover the 229 // PackageInstallerService portion of the parsing. 230 private fun restoreSessions(): List<PackageInstallerSession> { 231 val ret: MutableList<PackageInstallerSession> = ArrayList() 232 var fis: FileInputStream? = null 233 try { 234 fis = mSessionsFile.openRead() 235 val parser = Xml.resolvePullParser(fis) 236 var type: Int 237 while (parser.next().also { type = it } != XmlPullParser.END_DOCUMENT) { 238 if (type == XmlPullParser.START_TAG) { 239 val tag = parser.name 240 if (PackageInstallerSession.TAG_SESSION == tag) { 241 val session: PackageInstallerSession 242 try { 243 session = PackageInstallerSession.readFromXml( 244 parser, 245 mock(PackageInstallerService.InternalCallback::class.java), 246 mock(Context::class.java), 247 mMockPackageManagerInternal, 248 BackgroundThread.getHandler().looper, 249 mock(StagingManager::class.java), 250 mTmpDir, 251 mock(PackageSessionProvider::class.java), 252 mock(SilentUpdatePolicy::class.java) 253 ) 254 ret.add(session) 255 } catch (e: Exception) { 256 Slog.e("PackageInstallerSessionTest", "Exception ", e) 257 continue 258 } 259 } 260 } 261 } 262 } catch (_: FileNotFoundException) { 263 // Missing sessions are okay, probably first boot 264 } catch (_: IOException) { 265 } catch (_: XmlPullParserException) { 266 } finally { 267 IoUtils.closeQuietly(fis) 268 } 269 return ret 270 } 271 272 private fun assertSessionParamsEquivalent(expected: SessionParams, actual: SessionParams) { 273 assertThat(expected.mode).isEqualTo(actual.mode) 274 assertThat(expected.installFlags).isEqualTo(actual.installFlags) 275 assertThat(expected.installLocation).isEqualTo(actual.installLocation) 276 assertThat(expected.installReason).isEqualTo(actual.installReason) 277 assertThat(expected.sizeBytes).isEqualTo(actual.sizeBytes) 278 assertThat(expected.appPackageName).isEqualTo(actual.appPackageName) 279 assertThat(expected.appIcon).isEqualTo(actual.appIcon) 280 assertThat(expected.originatingUri).isEqualTo(actual.originatingUri) 281 assertThat(expected.originatingUid).isEqualTo(actual.originatingUid) 282 assertThat(expected.referrerUri).isEqualTo(actual.referrerUri) 283 assertThat(expected.abiOverride).isEqualTo(actual.abiOverride) 284 assertThat(expected.volumeUuid).isEqualTo(actual.volumeUuid) 285 assertThat(expected.permissionStates).isEqualTo(actual.permissionStates) 286 assertThat(expected.installerPackageName).isEqualTo(actual.installerPackageName) 287 assertThat(expected.isMultiPackage).isEqualTo(actual.isMultiPackage) 288 assertThat(expected.isStaged).isEqualTo(actual.isStaged) 289 } 290 291 private fun assertEquals( 292 expected: List<PackageInstallerSession>, 293 actual: List<PackageInstallerSession> 294 ) { 295 assertThat(expected).hasSize(actual.size) 296 expected.sortedBy { it.sessionId }.zip(actual.sortedBy { it.sessionId }) 297 .forEach { (expected, actual) -> 298 assertEquals(expected, actual) 299 } 300 } 301 302 private fun assertEquals(expected: PackageInstallerSession, actual: PackageInstallerSession) { 303 // Check both the restored params and an unparcelized variant to ensure parcelling works 304 assertSessionParamsEquivalent(expected.params, actual.params) 305 assertSessionParamsEquivalent(expected.params, actual.params.let { 306 val parcel = Parcel.obtain() 307 it.writeToParcel(parcel, 0) 308 parcel.setDataPosition(0) 309 SessionParams.CREATOR.createFromParcel(parcel).also { 310 parcel.recycle() 311 } 312 }) 313 314 assertThat(expected.sessionId).isEqualTo(actual.sessionId) 315 assertThat(expected.userId).isEqualTo(actual.userId) 316 assertThat(expected.installerUid).isEqualTo(actual.installerUid) 317 assertThat(expected.installerPackageName).isEqualTo(actual.installerPackageName) 318 assertInstallSourcesEquivalent(expected.installSource, actual.installSource) 319 assertThat(expected.stageDir.absolutePath).isEqualTo(actual.stageDir.absolutePath) 320 assertThat(expected.stageCid).isEqualTo(actual.stageCid) 321 assertThat(expected.isPrepared).isEqualTo(actual.isPrepared) 322 assertThat(expected.isStaged).isEqualTo(actual.isStaged) 323 assertThat(expected.isSessionApplied).isEqualTo(actual.isSessionApplied) 324 assertThat(expected.isSessionFailed).isEqualTo(actual.isSessionFailed) 325 assertThat(expected.isSessionReady).isEqualTo(actual.isSessionReady) 326 assertThat(expected.sessionErrorCode).isEqualTo(actual.sessionErrorCode) 327 assertThat(expected.sessionErrorMessage).isEqualTo(actual.sessionErrorMessage) 328 assertThat(expected.isPrepared).isEqualTo(actual.isPrepared) 329 assertThat(expected.isCommitted).isEqualTo(actual.isCommitted) 330 assertThat(expected.isPreapprovalRequested).isEqualTo(actual.isPreapprovalRequested) 331 assertThat(expected.createdMillis).isEqualTo(actual.createdMillis) 332 assertThat(expected.isSealed).isEqualTo(actual.isSealed) 333 assertThat(expected.isMultiPackage).isEqualTo(actual.isMultiPackage) 334 assertThat(expected.hasParentSessionId()).isEqualTo(actual.hasParentSessionId()) 335 assertThat(expected.parentSessionId).isEqualTo(actual.parentSessionId) 336 assertThat(expected.childSessionIds).asList() 337 .containsExactlyElementsIn(actual.childSessionIds.toList()) 338 assertThat(expected.preVerifiedDomains).isEqualTo(actual.preVerifiedDomains) 339 } 340 341 private fun assertInstallSourcesEquivalent(expected: InstallSource, actual: InstallSource) { 342 assertThat(expected.mInstallerPackageName).isEqualTo(actual.mInstallerPackageName) 343 assertThat(expected.mInitiatingPackageName).isEqualTo(actual.mInitiatingPackageName) 344 assertThat(expected.mOriginatingPackageName).isEqualTo(actual.mOriginatingPackageName) 345 } 346 }