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