Migrate AsyncTask to Coroutines
AsyncTask is deprecated in Java. Use Kotlin's coroutines instead
Bug: 182205982
Test: Manual. Sideload an APK and observe the staging dialog
Change-Id: I7e9394417be0ca3f8b5e12b1e31b8b1941290114
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt
index 4fcea5e..326e533 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt
@@ -60,6 +60,10 @@
import com.android.packageinstaller.v2.model.PackageUtil.isPermissionGranted
import java.io.File
import java.io.IOException
+import kotlinx.coroutines.DelicateCoroutinesApi
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.launch
class InstallRepository(private val context: Context) {
@@ -242,6 +246,7 @@
}
}
+ @OptIn(DelicateCoroutinesApi::class)
fun stageForInstall() {
val uri = intent.data
if (stagedSessionId != SessionInfo.INVALID_ID
@@ -290,13 +295,14 @@
return
}
}
- val listener: SessionStageListener = object : SessionStageListener {
- override fun onStagingSuccess(info: SessionInfo?) {
- //TODO: Verify if the returned sessionInfo should be used anywhere
- _stagingResult.value = InstallReady()
- }
- override fun onStagingFailure() {
+ sessionStager = SessionStager(context, uri, stagedSessionId)
+ GlobalScope.launch(Dispatchers.Main) {
+ val wasFileStaged = sessionStager!!.execute()
+
+ if (wasFileStaged) {
+ _stagingResult.value = InstallReady()
+ } else {
cleanupStagingSession()
_stagingResult.value = InstallAborted(
ABORT_REASON_INTERNAL_ERROR,
@@ -307,9 +313,6 @@
)
}
}
- sessionStager?.cancel(true)
- sessionStager = SessionStager(context, uri, stagedSessionId, listener)
- sessionStager?.execute()
}
}
@@ -845,10 +848,8 @@
return generateConfirmationSnippet()
}
- val stagingProgress: MutableLiveData<Int>
- get() = if (sessionStager != null) {
- sessionStager?.progress ?: MutableLiveData(0)
- } else MutableLiveData(0)
+ val stagingProgress: LiveData<Int>
+ get() = sessionStager?.progress ?: MutableLiveData(0)
companion object {
const val EXTRA_STAGED_SESSION_ID = "com.android.packageinstaller.extra.STAGED_SESSION_ID"
@@ -857,11 +858,6 @@
private val LOG_TAG = InstallRepository::class.java.simpleName
}
- interface SessionStageListener {
- fun onStagingSuccess(info: SessionInfo?)
- fun onStagingFailure()
- }
-
data class CallerInfo(val packageName: String?, val uid: Int)
data class AppOpRequestInfo(
val callingPackage: String?,
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/SessionStager.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/SessionStager.kt
index 015b95f..c9bfa17 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/SessionStager.kt
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/SessionStager.kt
@@ -20,55 +20,53 @@
import android.content.pm.PackageInstaller
import android.content.res.AssetFileDescriptor
import android.net.Uri
-import android.os.AsyncTask
import android.util.Log
+import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
-import com.android.packageinstaller.v2.model.InstallRepository.SessionStageListener
import java.io.IOException
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
class SessionStager internal constructor(
private val context: Context,
private val uri: Uri,
- private val stagedSessionId: Int,
- private val listener: SessionStageListener,
-) : AsyncTask<Void, Int, PackageInstaller.SessionInfo?>() {
+ private val stagedSessionId: Int
+) {
companion object {
private val LOG_TAG = SessionStager::class.java.simpleName
}
- val progress = MutableLiveData(0)
+ private val _progress = MutableLiveData(0)
+ val progress: LiveData<Int>
+ get() = _progress
- override fun doInBackground(vararg params: Void): PackageInstaller.SessionInfo? {
- val pi = context.packageManager.packageInstaller
+ suspend fun execute(): Boolean = withContext(Dispatchers.IO) {
+ val pi: PackageInstaller = context.packageManager.packageInstaller
+ var sessionInfo: PackageInstaller.SessionInfo?
try {
val session = pi.openSession(stagedSessionId)
context.contentResolver.openInputStream(uri).use { instream ->
session.setStagingProgress(0f)
if (instream == null) {
- return null
+ return@withContext false
}
val sizeBytes = getContentSizeBytes()
- progress.postValue(if (sizeBytes > 0) 0 else -1)
+ publishProgress(if (sizeBytes > 0) 0 else -1)
var totalRead: Long = 0
session.openWrite("PackageInstaller", 0, sizeBytes).use { out ->
val buffer = ByteArray(1024 * 1024)
while (true) {
val numRead = instream.read(buffer)
-
if (numRead == -1) {
session.fsync(out)
break
}
-
- if (isCancelled) {
- break
- }
-
out.write(buffer, 0, numRead)
+
if (sizeBytes > 0) {
totalRead += numRead.toLong()
val fraction = totalRead.toFloat() / sizeBytes.toFloat()
@@ -77,12 +75,21 @@
}
}
}
- return pi.getSessionInfo(stagedSessionId)!!
+ sessionInfo = pi.getSessionInfo(stagedSessionId)
}
-
} catch (e: Exception) {
Log.w(LOG_TAG, "Error staging apk from content URI", e)
- return null
+ sessionInfo = null
+ }
+
+ return@withContext if (sessionInfo == null
+ || !sessionInfo?.isActive!!
+ || sessionInfo?.resolvedBaseApkPath == null
+ ) {
+ Log.w(LOG_TAG, "Session info is invalid: $sessionInfo")
+ false
+ } else {
+ true
}
}
@@ -97,21 +104,7 @@
}
}
- override fun onPostExecute(sessionInfo: PackageInstaller.SessionInfo?) {
- if ((sessionInfo == null)
- || !sessionInfo.isActive
- || (sessionInfo.resolvedBaseApkPath == null)
- ) {
- Log.w(LOG_TAG, "Session info is invalid: $sessionInfo")
- listener.onStagingFailure()
- return
- }
- listener.onStagingSuccess(sessionInfo)
- }
-
- override fun onProgressUpdate(vararg progressVal: Int?) {
- if (progressVal.isNotEmpty()) {
- progress.value = progressVal[0]
- }
+ private suspend fun publishProgress(progressValue: Int) = withContext(Dispatchers.Main) {
+ _progress.value = progressValue
}
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/InstallViewModel.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/InstallViewModel.kt
index b141010..072fb2d 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/InstallViewModel.kt
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/InstallViewModel.kt
@@ -19,6 +19,7 @@
import android.app.Application
import android.content.Intent
import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.LiveData
import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.MutableLiveData
import com.android.packageinstaller.v2.model.InstallRepository
@@ -56,7 +57,7 @@
}
}
- val stagingProgress: MutableLiveData<Int>
+ val stagingProgress: LiveData<Int>
get() = repository.stagingProgress
private fun checkIfAllowedAndInitiateInstall() {