• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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