1 /*
<lambda>null2  * Copyright (C) 2018 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 com.android.tests.appmetadata.app
18 
19 import android.app.PendingIntent
20 import android.app.PendingIntent.FLAG_MUTABLE
21 import android.app.PendingIntent.FLAG_UPDATE_CURRENT
22 import android.content.BroadcastReceiver
23 import android.content.Context
24 import android.content.Intent
25 import android.content.IntentFilter
26 import android.content.pm.PackageInstaller
27 import android.content.pm.PackageInstaller.EXTRA_STATUS
28 import android.content.pm.PackageInstaller.EXTRA_STATUS_MESSAGE
29 import android.content.pm.PackageInstaller.STATUS_FAILURE_INVALID
30 import android.content.pm.PackageInstaller.STATUS_SUCCESS
31 import android.content.pm.PackageInstaller.Session
32 import android.content.pm.PackageInstaller.SessionParams.MODE_FULL_INSTALL
33 import android.content.pm.PackageManager
34 import android.os.PersistableBundle
35 import android.util.Log
36 import androidx.test.platform.app.InstrumentationRegistry
37 import java.io.File
38 import java.util.concurrent.LinkedBlockingQueue
39 import java.util.concurrent.TimeUnit
40 import org.junit.After
41 import org.junit.Assert.assertEquals
42 import org.junit.Assert.assertTrue
43 import org.junit.Before
44 import org.junit.Test
45 
46 open class AppMetadataDeviceTest {
47 
48     companion object {
49         const val TAG = "AppMetadataDeviceTest"
50 
51         const val TEST_APK_NAME = "CtsEmptyTestApp.apk"
52         const val TEST_APK_PACKAGE_NAME = "android.packageinstaller.emptytestapp.cts"
53         const val TEST_APK_LOCATION = "/data/local/tmp/cts/packageinstaller"
54 
55         const val INSTALL_ACTION_CB = "AppMetadataDeviceTest.install_cb"
56 
57         const val GLOBAL_TIMEOUT = 60000L
58 
59         const val TEST_FIELD = "testField"
60         const val TEST_VALUE = "testValue"
61     }
62 
63     val instrumentation = InstrumentationRegistry.getInstrumentation()
64     val uiAutomation = instrumentation.getUiAutomation()
65     val context: Context = instrumentation.targetContext
66     val pm: PackageManager = context.packageManager
67     val pi = pm.packageInstaller
68 
69     data class SessionResult(val status: Int?, val message: String?)
70 
71     private var installSessionResult = LinkedBlockingQueue<SessionResult>()
72 
73     private val receiver = object : BroadcastReceiver() {
74         override fun onReceive(context: Context, intent: Intent) {
75             val status = intent.getIntExtra(EXTRA_STATUS, STATUS_FAILURE_INVALID)
76             val msg = intent.getStringExtra(EXTRA_STATUS_MESSAGE)
77             Log.d(TAG, "status: $status, msg: $msg")
78             installSessionResult.offer(SessionResult(status, msg))
79         }
80     }
81 
82     @Before
83     fun registerInstallResultReceiver() {
84         context.registerReceiver(
85             receiver,
86             IntentFilter(INSTALL_ACTION_CB),
87             Context.RECEIVER_EXPORTED
88         )
89     }
90 
91     @After
92     fun unregisterInstallResultReceiver() {
93         try {
94             context.unregisterReceiver(receiver)
95         } catch (ignored: IllegalArgumentException) {
96         }
97     }
98 
99     protected fun getInstallSessionResult(timeout: Long = GLOBAL_TIMEOUT, ): SessionResult {
100         return installSessionResult.poll(timeout, TimeUnit.MILLISECONDS)
101             ?: SessionResult(null, "Fail to poll result")
102     }
103 
104     protected fun createSession(): Session {
105         val sessionParam = PackageInstaller.SessionParams(MODE_FULL_INSTALL)
106         val sessionId = pi.createSession(sessionParam)
107         return pi.openSession(sessionId)
108     }
109 
110     protected fun writeSession(session: Session, apkName: String) {
111         File(TEST_APK_LOCATION, apkName).inputStream().use { fileOnDisk ->
112             session.openWrite(apkName, 0, -1).use { sessionFile ->
113                 fileOnDisk.copyTo(sessionFile)
114             }
115         }
116     }
117 
118     protected fun commitSession(session: Session) {
119         var intent = Intent(INSTALL_ACTION_CB)
120             .setPackage(context.getPackageName())
121             .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
122         val pendingIntent = PendingIntent.getBroadcast(
123             context,
124             0,
125             intent,
126             FLAG_UPDATE_CURRENT or FLAG_MUTABLE
127         )
128         session.commit(pendingIntent.intentSender)
129         val result = getInstallSessionResult()
130         assertEquals(STATUS_SUCCESS, result.status)
131     }
132 
133     private fun setAppMetadata(session: Session, data: PersistableBundle?) {
134         try {
135             session.setAppMetadata(data)
136         } catch (e: Exception) {
137             session.abandon()
138             throw e
139         }
140     }
141 
142     @Test
143     fun installPackageWithAppMetadata() {
144         val bundle = PersistableBundle()
145         bundle.putString(TEST_FIELD, TEST_VALUE)
146         uiAutomation.adoptShellPermissionIdentity()
147         try {
148             val session = createSession()
149             writeSession(session, TEST_APK_NAME)
150             setAppMetadata(session, bundle)
151             commitSession(session)
152             val appMetadata = pm.getAppMetadata(TEST_APK_PACKAGE_NAME)
153             assertTrue(appMetadata.containsKey(TEST_FIELD))
154             assertEquals(appMetadata.getString(TEST_FIELD), TEST_VALUE)
155         } finally {
156             uiAutomation.dropShellPermissionIdentity()
157         }
158     }
159 }
160