blob: 67fa1bc320714787a26362174c1df92cc4bafeba [file] [log] [blame]
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.permission5.cts
import android.Manifest
import android.app.AppOpsManager
import android.app.Instrumentation
import android.content.AttributionSource
import android.content.ComponentName
import android.content.ContentValues
import android.content.Context
import android.content.ContextParams
import android.content.Intent
import android.content.pm.PackageManager.FEATURE_LEANBACK
import android.content.pm.PackageManager.FEATURE_TELEPHONY
import android.net.Uri
import android.os.Bundle
import android.os.Process
import android.os.RemoteCallback
import android.os.SystemClock
import android.os.UserHandle
import android.permission.PermissionManager
import android.platform.test.annotations.AppModeFull
import android.provider.CalendarContract
import android.provider.CallLog
import android.provider.ContactsContract
import android.provider.Telephony
import android.speech.RecognitionListener
import android.speech.SpeechRecognizer
import androidx.test.platform.app.InstrumentationRegistry
import com.android.compatibility.common.util.SystemUtil
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Assume.assumeFalse
import org.junit.Assume.assumeTrue
import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentMatcher
import org.mockito.Mockito.eq
import org.mockito.Mockito.inOrder
import org.mockito.Mockito.intThat
import org.mockito.Mockito.isNull
import org.mockito.Mockito.mock
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicReference
import java.util.concurrent.locks.ReentrantLock
import java.util.function.Consumer
@AppModeFull(reason = "Instant apps cannot hold READ_CONTACTS/READ_CALENDAR/READ_SMS/READ_CALL_LOG")
class RuntimePermissionsAppOpTrackingTest {
@Before
fun setUpTest() {
val appOpsManager = context.getSystemService(AppOpsManager::class.java)!!
SystemUtil.runWithShellPermissionIdentity {
appOpsManager.clearHistory()
appOpsManager.setHistoryParameters(
AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE,
SNAPSHOT_INTERVAL_MILLIS,
INTERVAL_COMPRESSION_MULTIPLIER)
appOpsManager.resetPackageOpsNoHistory(context.packageName)
appOpsManager.resetPackageOpsNoHistory(SHELL_PACKAGE_NAME)
appOpsManager.resetPackageOpsNoHistory(RECEIVER_PACKAGE_NAME)
appOpsManager.resetPackageOpsNoHistory(RECEIVER2_PACKAGE_NAME)
}
}
@After
fun tearDownTest() {
val appOpsManager = context.getSystemService(AppOpsManager::class.java)!!
SystemUtil.runWithShellPermissionIdentity {
appOpsManager.clearHistory()
appOpsManager.resetHistoryParameters()
}
}
@Test
@Throws(Exception::class)
fun testSelfContactsAccess() {
testSelfAccess(ContactsContract.Contacts.CONTENT_URI,
Manifest.permission.READ_CONTACTS)
}
@Test
@Throws(Exception::class)
fun testSelfCalendarAccess() {
testSelfAccess(CalendarContract.Calendars.CONTENT_URI,
Manifest.permission.READ_CALENDAR)
}
@Test
@Throws(Exception::class)
fun testSelfSmsAccess() {
assumeNotTv()
assumeHasTelephony()
testSelfAccess(Telephony.Sms.CONTENT_URI,
Manifest.permission.READ_SMS)
}
@Test
@Throws(Exception::class)
fun testSelfCallLogAccess() {
testSelfAccess(CallLog.Calls.CONTENT_URI,
Manifest.permission.READ_CALL_LOG)
}
@Throws(Exception::class)
private fun testSelfAccess(uri: Uri, permission: String) {
val context = createAttributionContext(ACCESSOR_ATTRIBUTION_TAG, null, null)
val beginEndMillis = System.currentTimeMillis()
context.contentResolver.query(uri, null, null, null)!!.close()
val endTimeMillis = System.currentTimeMillis()
assertNotRunningOpAccess(AppOpsManager.permissionToOp(permission)!!,
beginEndMillis, endTimeMillis, context.attributionSource,
/*accessorForeground*/ true, /*receiverForeground*/ false,
/*accessorTrusted*/ true, /*accessorAccessCount*/ 1,
/*receiverAccessCount*/ 0, /*checkAccessor*/ true,
/*fromDatasource*/ false)
}
@Test
@Throws(Exception::class)
fun testSelfCalendarWrite() {
testSelfWrite(CalendarContract.Calendars.CONTENT_URI,
Manifest.permission.WRITE_CALENDAR)
}
@Test
@Throws(Exception::class)
fun testSelfCallLogWrite() {
testSelfWrite(CallLog.Calls.CONTENT_URI,
Manifest.permission.WRITE_CALL_LOG)
}
@Throws(Exception::class)
private fun testSelfWrite(uri: Uri, permission: String) {
val context = createAttributionContext(ACCESSOR_ATTRIBUTION_TAG, null, null)
val beginEndMillis = System.currentTimeMillis()
context.contentResolver.insert(uri, ContentValues())
val endTimeMillis = System.currentTimeMillis()
assertNotRunningOpAccess(AppOpsManager.permissionToOp(permission)!!,
beginEndMillis, endTimeMillis, context.attributionSource,
/*accessorForeground*/ true, /*receiverForeground*/ false,
/*accessorTrusted*/ true, /*accessorAccessCount*/ 1,
/*receiverAccessCount*/ 0, /*checkAccessor*/ true,
/*fromDatasource*/ false)
}
@Test
@Throws(Exception::class)
fun testUntrustedContactsAccessAttributeToAnother() {
testUntrustedAccessAttributeToAnother(ContactsContract.Contacts.CONTENT_URI,
Manifest.permission.READ_CONTACTS)
}
@Test
@Throws(Exception::class)
fun testUntrustedCalendarAccessAttributeToAnother() {
testUntrustedAccessAttributeToAnother(CalendarContract.Calendars.CONTENT_URI,
Manifest.permission.READ_CALENDAR)
}
@Test
@Throws(Exception::class)
fun testUntrustedSmsAccessAttributeToAnother() {
assumeNotTv()
assumeHasTelephony()
testUntrustedAccessAttributeToAnother(Telephony.Sms.CONTENT_URI,
Manifest.permission.READ_SMS)
}
@Test
@Throws(Exception::class)
fun testUntrustedCallLogAccessAttributeToAnother() {
testUntrustedAccessAttributeToAnother(CallLog.Calls.CONTENT_URI,
Manifest.permission.READ_CALL_LOG)
}
@Throws(Exception::class)
private fun testUntrustedAccessAttributeToAnother(uri: Uri, permission: String) {
val context = createAttributionContext(ACCESSOR_ATTRIBUTION_TAG,
RECEIVER_PACKAGE_NAME, RECEIVER_ATTRIBUTION_TAG)
val beginEndMillis = System.currentTimeMillis()
context.contentResolver.query(uri, null, null, null)!!.close()
val endTimeMillis = System.currentTimeMillis()
assertNotRunningOpAccess(AppOpsManager.permissionToOp(permission)!!,
beginEndMillis, endTimeMillis, context.attributionSource,
/*accessorForeground*/ true, /*receiverForeground*/ false,
/*accessorTrusted*/ false, /*accessorAccessCount*/ 1,
/*receiverAccessCount*/ 1, /*checkAccessor*/ false,
/*fromDatasource*/ false)
}
@Test
@Throws(Exception::class)
fun testUntrustedContactsAccessAttributeToAnotherThroughIntermediary() {
testUntrustedAccessAttributeToAnotherThroughIntermediary(
ContactsContract.Contacts.CONTENT_URI,
Manifest.permission.READ_CONTACTS)
}
@Test
@Throws(Exception::class)
fun testUntrustedCalendarAccessAttributeToAnotherThroughIntermediary() {
testUntrustedAccessAttributeToAnotherThroughIntermediary(
CalendarContract.Calendars.CONTENT_URI,
Manifest.permission.READ_CALENDAR)
}
@Test
@Throws(Exception::class)
fun testUntrustedSmsAccessAttributeToAnotherThroughIntermediary() {
assumeNotTv()
assumeHasTelephony()
testUntrustedAccessAttributeToAnotherThroughIntermediary(
Telephony.Sms.CONTENT_URI,
Manifest.permission.READ_SMS)
}
@Test
@Throws(Exception::class)
fun testUntrustedCallLogAccessAttributeToAnotherThroughIntermediary() {
testUntrustedAccessAttributeToAnotherThroughIntermediary(
CallLog.Calls.CONTENT_URI,
Manifest.permission.READ_CALL_LOG)
}
@Throws(Exception::class)
private fun testUntrustedAccessAttributeToAnotherThroughIntermediary(
uri: Uri,
permission: String
) {
runWithAuxiliaryApps {
val nextAttributionSource = startBlamedAppActivity()
val intermediaryContext = context.createContext(ContextParams.Builder()
.setNextAttributionSource(nextAttributionSource)
.setAttributionTag(ACCESSOR_ATTRIBUTION_TAG)
.build())
val beginEndMillis = System.currentTimeMillis()
intermediaryContext.contentResolver.query(uri, null, null, null)!!.close()
val endTimeMillis = System.currentTimeMillis()
// Assert first stage access
assertNotRunningOpAccess(AppOpsManager.permissionToOp(permission)!!,
beginEndMillis, endTimeMillis, intermediaryContext.attributionSource,
/*accessorForeground*/ true, /*receiverForeground*/ true,
/*accessorTrusted*/ false, /*accessorAccessCount*/ 1,
/*receiverAccessCount*/ 1, /*checkAccessor*/ false,
/*fromDatasource*/ false)
// Assert second stage access
assertNotRunningOpAccess(AppOpsManager.permissionToOp(permission)!!,
beginEndMillis, endTimeMillis, nextAttributionSource,
/*accessorForeground*/ true, /*receiverForeground*/ false,
/*accessorTrusted*/ false, /*accessorAccessCount*/ 1,
/*receiverAccessCount*/ 1, /*checkAccessor*/ false,
/*fromDatasource*/ false)
}
}
@Test(expected = SecurityException::class)
fun testCannotForgeAttributionSource() {
val receiverSource = AttributionSource(context
.packageManager.getPackageUid(RECEIVER2_PACKAGE_NAME, 0),
RECEIVER2_PACKAGE_NAME, RECEIVER2_ATTRIBUTION_TAG, null, AttributionSource(
context.packageManager.getPackageUid(RECEIVER_PACKAGE_NAME, 0),
RECEIVER_PACKAGE_NAME, RECEIVER_ATTRIBUTION_TAG))
val intermediaryContext = context.createContext(ContextParams.Builder()
.setNextAttributionSource(receiverSource)
.setAttributionTag(ACCESSOR_ATTRIBUTION_TAG)
.build())
intermediaryContext.contentResolver.query(CallLog.Calls.CONTENT_URI, null,
null, null)!!.close()
}
@Test(expected = SecurityException::class)
fun testCannotAppendToForgeAttributionSource() {
runWithAuxiliaryApps {
val nextAttributionSource = startBlamedAppActivity()
val untrustedAttributionSource = AttributionSource(context
.packageManager.getPackageUid(RECEIVER2_PACKAGE_NAME, 0),
RECEIVER2_PACKAGE_NAME, RECEIVER2_ATTRIBUTION_TAG, null,
nextAttributionSource)
val intermediaryContext = context.createContext(ContextParams.Builder()
.setNextAttributionSource(untrustedAttributionSource)
.setAttributionTag(ACCESSOR_ATTRIBUTION_TAG)
.build())
intermediaryContext.contentResolver.query(CallLog.Calls.CONTENT_URI, null,
null, null)!!.close()
}
}
@Test
@Throws(Exception::class)
fun testTrustedAccessContactsAttributeToAnother() {
testTrustedAccessAttributeToAnother(ContactsContract.Contacts.CONTENT_URI,
Manifest.permission.READ_CONTACTS)
}
@Test
@Throws(Exception::class)
fun testTrustedAccessCalendarAttributeToAnother() {
testTrustedAccessAttributeToAnother(CalendarContract.Calendars.CONTENT_URI,
Manifest.permission.READ_CALENDAR)
}
@Test
@Throws(Exception::class)
fun testTrustedAccessSmsAttributeToAnother() {
assumeNotTv()
assumeHasTelephony()
testTrustedAccessAttributeToAnother(Telephony.Sms.CONTENT_URI,
Manifest.permission.READ_SMS)
}
@Test
@Throws(Exception::class)
fun testTrustedAccessCallLogAttributeToAnother() {
testTrustedAccessAttributeToAnother(CallLog.Calls.CONTENT_URI,
Manifest.permission.READ_CALL_LOG)
}
@Throws(Exception::class)
private fun testTrustedAccessAttributeToAnother(uri: Uri, permission: String) {
val context = createAttributionContext(ACCESSOR_ATTRIBUTION_TAG,
RECEIVER_PACKAGE_NAME, RECEIVER_ATTRIBUTION_TAG)
val beginEndMillis = System.currentTimeMillis()
SystemUtil.runWithShellPermissionIdentity {
context.contentResolver.query(uri, null, null, null)!!.close()
}
val endTimeMillis = System.currentTimeMillis()
// Calculate the shellUid to account for running this from a secondary user.
val shellUid = UserHandle.getUid(Process.myUserHandle().identifier,
UserHandle.getAppId(Process.SHELL_UID))
// Since we use adopt the shell permission identity we need to adjust
// the permission identity to have the shell as the accessor.
assertNotRunningOpAccess(AppOpsManager.permissionToOp(permission)!!,
beginEndMillis, endTimeMillis, AttributionSource(shellUid,
SHELL_PACKAGE_NAME, context.attributionTag, null,
context.attributionSource.next),
/*accessorForeground*/ false, /*receiverForeground*/ false,
/*accessorTrusted*/ true, /*accessorAccessCount*/ 1,
/*receiverAccessCount*/ 1, /*checkAccessor*/ false,
/*fromDatasource*/ false)
}
@Test
@Throws(Exception::class)
fun testMicRecognitionInjectRecoWithoutAttribution() {
runWithAuxiliaryApps {
startBlamedAppActivity()
val context = createAttributionContext(ACCESSOR_ATTRIBUTION_TAG,
RECEIVER_PACKAGE_NAME, RECEIVER_ATTRIBUTION_TAG)
val listener = mock(AppOpsManager.OnOpActiveChangedListener::class.java)
val appopsManager = context.getSystemService(AppOpsManager::class.java)
SystemUtil.runWithShellPermissionIdentity<Unit> {
appopsManager!!.startWatchingActive(arrayOf(AppOpsManager.OPSTR_RECORD_AUDIO),
context.mainExecutor, listener)
}
val speechStartTime = System.currentTimeMillis()
val recognizerRef = AtomicReference<SpeechRecognizer>()
var currentOperationComplete = CountDownLatch(1)
instrumentation.runOnMainSync {
val recognizer = SpeechRecognizer.createSpeechRecognizer(context,
ComponentName(RECEIVER2_PACKAGE_NAME, RECOGNITION_SERVICE))
recognizer.setRecognitionListener(object : RecognitionListener {
override fun onReadyForSpeech(params: Bundle?) {}
override fun onRmsChanged(rmsdB: Float) {}
override fun onBufferReceived(buffer: ByteArray?) {
currentOperationComplete.countDown()
}
override fun onPartialResults(partialResults: Bundle?) {}
override fun onEvent(eventType: Int, params: Bundle?) {}
override fun onError(error: Int) {}
override fun onResults(results: Bundle?) {}
override fun onBeginningOfSpeech() {}
override fun onEndOfSpeech() {}
})
val recoIntent = Intent()
recoIntent.putExtra(OPERATION, OPERATION_INJECT_RECO_WITHOUT_ATTRIBUTION)
recognizer.startListening(recoIntent)
recognizerRef.set(recognizer)
}
try {
currentOperationComplete.await(ASYNC_OPERATION_TIMEOUT_MILLIS,
TimeUnit.MILLISECONDS)
val op = AppOpsManager.permissionToOp(Manifest.permission.RECORD_AUDIO)!!
assertRunningOpAccess(op, speechStartTime, System.currentTimeMillis(),
AttributionSource(context.packageManager.getPackageUid(
RECEIVER2_PACKAGE_NAME, 0), RECEIVER2_PACKAGE_NAME,
/*attributionTag*/ null, null, context.attributionSource),
/*accessorForeground*/ true, /*receiverForeground*/ true,
/*accessorTrusted*/ false, /*accessorAccessCount*/ 1,
/*receiverAccessCount*/ 1, /*checkAccessor*/ true,
/*fromDatasource*/ false)
assertRunningOpAccess(op, speechStartTime, System.currentTimeMillis(),
context.attributionSource, /*accessorForeground*/ true,
/*receiverForeground*/ true, /*accessorTrusted*/ false,
/*accessorAccessCount*/ 0, /*receiverAccessCount*/ 1,
/*checkAccessor*/ false, /*fromDatasource*/ false)
// Finish recon and check if all ops are finished
currentOperationComplete = CountDownLatch(1)
instrumentation.runOnMainSync { recognizerRef.get().cancel() }
currentOperationComplete.await(ASYNC_OPERATION_TIMEOUT_MILLIS,
TimeUnit.MILLISECONDS)
val recognizerUid = context.packageManager.getPackageUid(
RECEIVER2_PACKAGE_NAME, 0)
assertNotRunningOpAccess(op, speechStartTime, System.currentTimeMillis(),
AttributionSource(recognizerUid, RECEIVER2_PACKAGE_NAME,
/*attributionTag*/ null, null, context.attributionSource),
/*accessorForeground*/ true, /*receiverForeground*/ true,
/*accessorTrusted*/ false, /*accessorAccessCount*/ 1,
/*receiverAccessCount*/ 1, /*checkAccessor*/ true,
/*fromDatasource*/ false)
assertNotRunningOpAccess(op, speechStartTime, System.currentTimeMillis(),
context.attributionSource, /*accessorForeground*/ true,
/*receiverForeground*/ true, /*accessorTrusted*/ false,
/*accessorAccessCount*/ 0, /*receiverAccessCount*/ 1,
/*checkAccessor*/ false, /*fromDatasource*/ false)
var attributionChainId: Int? = null
val inOrder = inOrder(listener)
val attributionChainIdMatcher = ArgumentMatcher<Int> {
if (attributionChainId == null) {
attributionChainId = it
return@ArgumentMatcher true
} else {
return@ArgumentMatcher (attributionChainId == it)
}
}
val receiverUid = context.packageManager.getPackageUid(
RECEIVER_PACKAGE_NAME, 0)
inOrder.verify(listener).onOpActiveChanged(eq(AppOpsManager.OPSTR_RECORD_AUDIO),
eq(recognizerUid), eq(RECEIVER2_PACKAGE_NAME), isNull(), eq(true),
eq(AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR),
intThat(attributionChainIdMatcher))
inOrder.verify(listener).onOpActiveChanged(eq(AppOpsManager.OPSTR_RECORD_AUDIO),
eq(Process.myUid()), eq(context.packageName), eq(ACCESSOR_ATTRIBUTION_TAG),
eq(true), eq(AppOpsManager.ATTRIBUTION_FLAG_INTERMEDIARY),
intThat(attributionChainIdMatcher))
inOrder.verify(listener).onOpActiveChanged(eq(AppOpsManager.OPSTR_RECORD_AUDIO),
eq(receiverUid), eq(RECEIVER_PACKAGE_NAME), eq(RECEIVER_ATTRIBUTION_TAG),
eq(true), eq(AppOpsManager.ATTRIBUTION_FLAG_RECEIVER),
intThat(attributionChainIdMatcher))
inOrder.verify(listener).onOpActiveChanged(eq(AppOpsManager.OPSTR_RECORD_AUDIO),
eq(recognizerUid), eq(RECEIVER2_PACKAGE_NAME), isNull(), eq(false),
eq(AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR),
intThat(attributionChainIdMatcher))
inOrder.verify(listener).onOpActiveChanged(eq(AppOpsManager.OPSTR_RECORD_AUDIO),
eq(Process.myUid()), eq(context.packageName), eq(ACCESSOR_ATTRIBUTION_TAG),
eq(false), eq(AppOpsManager.ATTRIBUTION_FLAG_INTERMEDIARY),
intThat(attributionChainIdMatcher))
inOrder.verify(listener).onOpActiveChanged(eq(AppOpsManager.OPSTR_RECORD_AUDIO),
eq(receiverUid), eq(RECEIVER_PACKAGE_NAME), eq(RECEIVER_ATTRIBUTION_TAG),
eq(false), eq(AppOpsManager.ATTRIBUTION_FLAG_RECEIVER),
intThat(attributionChainIdMatcher))
} finally {
// Take down the recognition service
instrumentation.runOnMainSync { recognizerRef.get().destroy() }
}
}
}
@Test
@Throws(Exception::class)
fun testMicRecognitionMicRecoWithAttribution() {
runWithAuxiliaryApps {
startBlamedAppActivity()
val context = createAttributionContext(ACCESSOR_ATTRIBUTION_TAG,
RECEIVER_PACKAGE_NAME, RECEIVER_ATTRIBUTION_TAG)
val listener = mock(AppOpsManager.OnOpActiveChangedListener::class.java)
val appopsManager = context.getSystemService(AppOpsManager::class.java)
SystemUtil.runWithShellPermissionIdentity<Unit> {
appopsManager!!.startWatchingActive(arrayOf(AppOpsManager.OPSTR_RECORD_AUDIO),
context.mainExecutor, listener)
}
val speechStartTime = System.currentTimeMillis()
val recognizerRef = AtomicReference<SpeechRecognizer>()
var currentOperationComplete = CountDownLatch(1)
instrumentation.runOnMainSync {
val recognizer = SpeechRecognizer.createSpeechRecognizer(context,
ComponentName(RECEIVER2_PACKAGE_NAME, RECOGNITION_SERVICE))
recognizer.setRecognitionListener(object : RecognitionListener {
override fun onReadyForSpeech(params: Bundle?) {}
override fun onRmsChanged(rmsdB: Float) {}
override fun onBufferReceived(buffer: ByteArray?) {
currentOperationComplete.countDown()
}
override fun onPartialResults(partialResults: Bundle?) {}
override fun onEvent(eventType: Int, params: Bundle?) {}
override fun onError(error: Int) {}
override fun onResults(results: Bundle?) {}
override fun onBeginningOfSpeech() {}
override fun onEndOfSpeech() {}
})
val recoIntent = Intent()
recoIntent.putExtra(OPERATION, OPERATION_MIC_RECO_WITH_ATTRIBUTION)
recognizer.startListening(recoIntent)
recognizerRef.set(recognizer)
}
try {
currentOperationComplete.await(ASYNC_OPERATION_TIMEOUT_MILLIS,
TimeUnit.MILLISECONDS)
val op = AppOpsManager.permissionToOp(Manifest.permission.RECORD_AUDIO)!!
assertRunningOpAccess(op, speechStartTime, System.currentTimeMillis(),
AttributionSource(context.packageManager.getPackageUid(
RECEIVER2_PACKAGE_NAME, 0), RECEIVER2_PACKAGE_NAME,
/*attributionTag*/ null, null, context.attributionSource),
/*accessorForeground*/ true, /*receiverForeground*/ true,
/*accessorTrusted*/ true, /*accessorAccessCount*/ 1,
/*receiverAccessCount*/ 1, /*checkAccessor*/ true,
/*fromDatasource*/ true)
assertRunningOpAccess(op, speechStartTime, System.currentTimeMillis(),
context.attributionSource, /*accessorForeground*/ true,
/*receiverForeground*/ true, /*accessorTrusted*/ true,
/*accessorAccessCount*/ 0, /*receiverAccessCount*/ 1,
/*checkAccessor*/ false, /*fromDatasource*/ true)
// Finish recon and check if all ops are finished
currentOperationComplete = CountDownLatch(1)
instrumentation.runOnMainSync { recognizerRef.get().cancel() }
currentOperationComplete.await(ASYNC_OPERATION_TIMEOUT_MILLIS,
TimeUnit.MILLISECONDS)
val recognizerUid = context.packageManager.getPackageUid(
RECEIVER2_PACKAGE_NAME, 0)
assertNotRunningOpAccess(op, speechStartTime, System.currentTimeMillis(),
AttributionSource(recognizerUid, RECEIVER2_PACKAGE_NAME,
/*attributionTag*/ null, null, context.attributionSource),
/*accessorForeground*/ true, /*receiverForeground*/ true,
/*accessorTrusted*/ true, /*accessorAccessCount*/ 1,
/*receiverAccessCount*/ 1, /*checkAccessor*/ true,
/*fromDatasource*/ true)
assertNotRunningOpAccess(op, speechStartTime, System.currentTimeMillis(),
context.attributionSource, /*accessorForeground*/ true,
/*receiverForeground*/ true, /*accessorTrusted*/ true,
/*accessorAccessCount*/ 0, /*receiverAccessCount*/ 1,
/*checkAccessor*/ false, /*fromDatasource*/ true)
var attributionChainId: Int? = null
val inOrder = inOrder(listener)
val attributionChainIdMatcher = ArgumentMatcher<Int> {
if (attributionChainId == null) {
attributionChainId = it
return@ArgumentMatcher true
} else {
return@ArgumentMatcher (attributionChainId == it)
}
}
val receiverUid = context.packageManager.getPackageUid(
RECEIVER_PACKAGE_NAME, 0)
inOrder.verify(listener).onOpActiveChanged(eq(AppOpsManager.OPSTR_RECORD_AUDIO),
eq(recognizerUid), eq(RECEIVER2_PACKAGE_NAME), isNull(), eq(true),
eq(AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR or ATTRIBUTION_FLAG_TRUSTED),
intThat(attributionChainIdMatcher))
inOrder.verify(listener).onOpActiveChanged(eq(AppOpsManager.OPSTR_RECORD_AUDIO),
eq(Process.myUid()), eq(context.packageName), eq(ACCESSOR_ATTRIBUTION_TAG),
eq(true), eq(AppOpsManager.ATTRIBUTION_FLAG_INTERMEDIARY or
ATTRIBUTION_FLAG_TRUSTED), intThat(attributionChainIdMatcher))
inOrder.verify(listener).onOpActiveChanged(eq(AppOpsManager.OPSTR_RECORD_AUDIO),
eq(receiverUid), eq(RECEIVER_PACKAGE_NAME), eq(RECEIVER_ATTRIBUTION_TAG),
eq(true), eq(AppOpsManager.ATTRIBUTION_FLAG_RECEIVER or
ATTRIBUTION_FLAG_TRUSTED), intThat(attributionChainIdMatcher))
inOrder.verify(listener).onOpActiveChanged(eq(AppOpsManager.OPSTR_RECORD_AUDIO),
eq(recognizerUid), eq(RECEIVER2_PACKAGE_NAME), isNull(), eq(false),
eq(AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR or ATTRIBUTION_FLAG_TRUSTED),
intThat(attributionChainIdMatcher))
inOrder.verify(listener).onOpActiveChanged(eq(AppOpsManager.OPSTR_RECORD_AUDIO),
eq(Process.myUid()), eq(context.packageName), eq(ACCESSOR_ATTRIBUTION_TAG),
eq(false), eq(AppOpsManager.ATTRIBUTION_FLAG_INTERMEDIARY or
ATTRIBUTION_FLAG_TRUSTED), intThat(attributionChainIdMatcher))
inOrder.verify(listener).onOpActiveChanged(eq(AppOpsManager.OPSTR_RECORD_AUDIO),
eq(receiverUid), eq(RECEIVER_PACKAGE_NAME), eq(RECEIVER_ATTRIBUTION_TAG),
eq(false), eq(AppOpsManager.ATTRIBUTION_FLAG_RECEIVER or
ATTRIBUTION_FLAG_TRUSTED), intThat(attributionChainIdMatcher))
} finally {
// Take down the recognition service
instrumentation.runOnMainSync { recognizerRef.get().destroy() }
}
}
}
fun runWithAuxiliaryApps(worker: () -> Unit) {
ensureAuxiliaryAppsNotRunningAndNoResidualProcessState()
try {
worker.invoke()
} finally {
ensureAuxiliaryAppsNotRunningAndNoResidualProcessState()
}
}
companion object {
private const val ASYNC_OPERATION_TIMEOUT_MILLIS: Long = 5000 // 5 sec
private const val INTERVAL_COMPRESSION_MULTIPLIER = 10
private const val SNAPSHOT_INTERVAL_MILLIS: Long = 1000
val SHELL_PACKAGE_NAME = "com.android.shell"
val RECEIVER_PACKAGE_NAME = "android.permission5.cts.blamed"
val BRING_TO_FOREGROUND_ACTIVITY =
"android.permission5.cts.blamed.BringToForegroundActivity"
val RECOGNITION_SERVICE = "android.permission5.cts.blamed2.MyRecognitionService"
val REMOTE_CALLBACK = "remote_callback"
val ATTRIBUTION_SOURCE = "attribution_source"
val ACCESSOR_ATTRIBUTION_TAG = "accessor_attribution_tag"
val RECEIVER2_PACKAGE_NAME = "android.permission5.cts.blamed2"
val RECEIVER_ATTRIBUTION_TAG = "receiver_attribution_tag"
val RECEIVER2_ATTRIBUTION_TAG = "receiver2_attribution_tag"
val OPERATION = "operation"
val OPERATION_MIC_RECO_WITH_ATTRIBUTION = "operation:mic_reco_with_attribution"
val OPERATION_INJECT_RECO_WITHOUT_ATTRIBUTION = "operation:inject_reco_without_attribution"
val ATTRIBUTION_FLAG_TRUSTED = 0x8
private val context: Context
get() = InstrumentationRegistry.getInstrumentation().getContext()
private val instrumentation: Instrumentation
get() = InstrumentationRegistry.getInstrumentation()
private val isTv = context.packageManager.hasSystemFeature(FEATURE_LEANBACK)
private val isTel = context.packageManager.hasSystemFeature(FEATURE_TELEPHONY)
fun ensureAuxiliaryAppsNotRunningAndNoResidualProcessState() {
SystemUtil.runShellCommand("am force-stop $RECEIVER_PACKAGE_NAME")
SystemUtil.runShellCommand("am force-stop $RECEIVER2_PACKAGE_NAME")
SystemClock.sleep(ASYNC_OPERATION_TIMEOUT_MILLIS)
}
@Throws(Exception::class)
private fun assertRunningOpAccess(
op: String,
beginEndMillis: Long,
endTimeMillis: Long,
attributionSource: AttributionSource,
accessorForeground: Boolean,
receiverForeground: Boolean,
accessorTrusted: Boolean,
accessorAccessCount: Int,
receiverAccessCount: Int,
checkAccessor: Boolean,
fromDatasource: Boolean
) {
assertOpAccess(op, beginEndMillis, endTimeMillis, attributionSource,
accessorForeground, receiverForeground, accessorTrusted,
/*assertRunning*/ true, accessorAccessCount, receiverAccessCount,
checkAccessor, fromDatasource)
}
@Throws(Exception::class)
private fun assertNotRunningOpAccess(
op: String,
beginEndMillis: Long,
endTimeMillis: Long,
attributionSource: AttributionSource,
accessorForeground: Boolean,
receiverForeground: Boolean,
accessorTrusted: Boolean,
accessorAccessCount: Int,
receiverAccessCount: Int,
checkAccessor: Boolean,
fromDatasource: Boolean
) {
assertOpAccess(op, beginEndMillis, endTimeMillis, attributionSource,
accessorForeground, receiverForeground, accessorTrusted,
/*assertRunning*/ false, accessorAccessCount, receiverAccessCount,
checkAccessor, fromDatasource)
}
@Throws(Exception::class)
private fun assertOpAccess(
op: String,
beginEndMillis: Long,
endTimeMillis: Long,
attributionSource: AttributionSource,
accessorForeground: Boolean,
receiverForeground: Boolean,
accessorTrusted: Boolean,
assertRunning: Boolean,
accessorAccessCount: Int,
receiverAccessCount: Int,
checkAccessor: Boolean,
fromDatasource: Boolean
) {
assertLastOpAccess(op, beginEndMillis, endTimeMillis, attributionSource,
accessorForeground, receiverForeground, accessorTrusted, assertRunning,
checkAccessor, fromDatasource)
assertHistoricalOpAccess(op, attributionSource, accessorForeground,
receiverForeground, accessorTrusted, accessorAccessCount, receiverAccessCount,
checkAccessor, fromDatasource)
}
private fun assertLastOpAccess(
op: String,
beginEndMillis: Long,
endTimeMillis: Long,
attributionSource: AttributionSource,
accessorForeground: Boolean,
receiverForeground: Boolean,
accessorTrusted: Boolean,
assertRunning: Boolean,
checkAccessor: Boolean,
fromDatasource: Boolean
) {
val appOpsManager = context.getSystemService(AppOpsManager::class.java)!!
val allPackagesOps: MutableList<AppOpsManager.PackageOps?> = ArrayList()
SystemUtil.runWithShellPermissionIdentity<Boolean> {
allPackagesOps.addAll(appOpsManager.getPackagesForOps(arrayOf(op)))
}
if (checkAccessor) {
assertLastAccessorOps(op, beginEndMillis, endTimeMillis, attributionSource,
accessorForeground, accessorTrusted, assertRunning, fromDatasource,
allPackagesOps)
} else {
assertNotLastAccessorOps(op, attributionSource, allPackagesOps)
}
if (attributionSource.next != null) {
assertLastReceiverOps(op, beginEndMillis, endTimeMillis, attributionSource,
receiverForeground, accessorTrusted, assertRunning, allPackagesOps)
}
}
@Throws(Exception::class)
private fun assertHistoricalOpAccess(
op: String,
attributionSource: AttributionSource,
accessorForeground: Boolean,
receiverForeground: Boolean,
accessorTrusted: Boolean,
accessorAccessCount: Int,
receiverAccessCount: Int,
checkAccessor: Boolean,
fromDatasource: Boolean
) {
val appOpsManager = context.getSystemService(AppOpsManager::class.java)!!
val request = AppOpsManager.HistoricalOpsRequest.Builder(0, Long.MAX_VALUE)
.setOpNames(listOf(op))
.build()
val historicalOpsRef = AtomicReference<AppOpsManager.HistoricalOps>()
val lock = ReentrantLock()
val condition = lock.newCondition()
SystemUtil.runWithShellPermissionIdentity {
appOpsManager.getHistoricalOps(request, context.mainExecutor,
Consumer { historicalOps: AppOpsManager.HistoricalOps ->
historicalOpsRef.set(historicalOps)
lock.lock()
try {
condition.signalAll()
} finally {
lock.unlock()
}
})
}
lock.lock()
try {
condition.await(ASYNC_OPERATION_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
} finally {
lock.unlock()
}
val historicalOps = historicalOpsRef.get()
if (checkAccessor) {
assertHistoricalAccessorOps(op, attributionSource, accessorForeground,
accessorTrusted, fromDatasource, accessorAccessCount, historicalOps)
} else {
assertNoHistoricalAccessorOps(op, attributionSource, historicalOps)
}
if (attributionSource.next != null) {
assertHistoricalReceiverOps(op, attributionSource, receiverForeground,
accessorTrusted, receiverAccessCount, historicalOps)
}
}
private fun assertLastAccessorOps(
op: String,
beginEndMillis: Long,
endTimeMillis: Long,
attributionSource: AttributionSource,
accessorForeground: Boolean,
accessorTrusted: Boolean,
assertRunning: Boolean,
fromDatasource: Boolean,
allPackagesOps: List<AppOpsManager.PackageOps?>
) {
val accessorPackageOps = findPackageOps(attributionSource.uid,
attributionSource.packageName!!, allPackagesOps)
for (opEntry in accessorPackageOps!!.ops) {
if (!op.equals(opEntry.opStr)) {
continue
}
val attributedOpEntry = opEntry.attributedOpEntries[
attributionSource.attributionTag]
if (attributionSource.next == null) {
// Access for ourselves
assertLastAccessInRange(attributedOpEntry!!, beginEndMillis, endTimeMillis,
AppOpsManager.OP_FLAG_SELF, accessorForeground, assertRunning)
} else if (accessorTrusted) {
// Access for others and we are trusted. If we got the data from a datasource
// the latter is the proxy and we proxied, otherwise we are the proxy.
if (fromDatasource) {
assertLastAccessInRange(attributedOpEntry!!, beginEndMillis, endTimeMillis,
AppOpsManager.OP_FLAG_TRUSTED_PROXIED, accessorForeground,
assertRunning)
} else {
assertLastAccessInRange(attributedOpEntry!!, beginEndMillis, endTimeMillis,
AppOpsManager.OP_FLAG_TRUSTED_PROXY, accessorForeground,
assertRunning)
}
} else {
// Access for others and we are not trusted.
assertLastAccessInRange(attributedOpEntry!!, beginEndMillis, endTimeMillis,
AppOpsManager.OP_FLAG_UNTRUSTED_PROXY, accessorForeground,
assertRunning)
}
}
}
private fun assertNotLastAccessorOps(
op: String,
attributionSource: AttributionSource,
allPackagesOps: List<AppOpsManager.PackageOps?>
) {
val accessorPackageOps = findPackageOps(attributionSource.uid,
attributionSource.packageName!!, allPackagesOps) ?: return
for (opEntry in accessorPackageOps.ops) {
if (!op.equals(opEntry.opStr)) {
continue
}
val attributedOpEntry = opEntry.attributedOpEntries[
attributionSource.attributionTag]
if (attributedOpEntry != null) {
assertThat(attributedOpEntry.getLastAccessBackgroundTime(
AppOpsManager.OP_FLAG_SELF
or AppOpsManager.OP_FLAG_UNTRUSTED_PROXY
or AppOpsManager.OP_FLAG_TRUSTED_PROXY)).isEqualTo(-1)
assertThat(attributedOpEntry.getLastAccessBackgroundTime(
AppOpsManager.OP_FLAG_SELF
or AppOpsManager.OP_FLAG_UNTRUSTED_PROXY
or AppOpsManager.OP_FLAG_TRUSTED_PROXY)).isEqualTo(-1)
}
}
}
private fun assertHistoricalAccessorOps(
op: String,
attributionSource: AttributionSource,
accessorForeground: Boolean,
accessorTrusted: Boolean,
fromDatasource: Boolean,
assertedAccessCount: Int,
historicalOps: AppOpsManager.HistoricalOps
) {
val accessorPackageOps = findPackageOps(
attributionSource.uid, attributionSource.packageName!!,
historicalOps)
val attributedPackageOps = accessorPackageOps?.getAttributedOps(
attributionSource.attributionTag)
val attributedPackageOp = attributedPackageOps!!.getOp(op)
if (attributionSource.next == null) {
// Access for ourselves
assertAccessCount(attributedPackageOp!!, AppOpsManager.OP_FLAG_SELF,
accessorForeground, assertedAccessCount)
} else if (accessorTrusted) {
// Access for others and we are trusted. If we got the data from a datasource it
// would blame the accessor in a trusted way
if (fromDatasource) {
assertAccessCount(attributedPackageOp!!, AppOpsManager.OP_FLAG_TRUSTED_PROXIED,
accessorForeground, assertedAccessCount)
} else {
assertAccessCount(attributedPackageOp!!, AppOpsManager.OP_FLAG_TRUSTED_PROXY,
accessorForeground, assertedAccessCount)
}
} else {
// Access for others and we are not trusted
assertAccessCount(attributedPackageOp!!, AppOpsManager.OP_FLAG_UNTRUSTED_PROXY,
accessorForeground, assertedAccessCount)
}
}
private fun assertNoHistoricalAccessorOps(
op: String,
attributionSource: AttributionSource,
historicalOps: AppOpsManager.HistoricalOps
) {
val accessorPackageOps = findPackageOps(
attributionSource.uid, attributionSource.packageName!!,
historicalOps)
val attributedPackageOps = accessorPackageOps?.getAttributedOps(
attributionSource.attributionTag) ?: return
val attributedPackageOp = attributedPackageOps.getOp(op)
if (attributedPackageOp != null) {
assertThat(attributedPackageOp.getBackgroundAccessCount(
AppOpsManager.OP_FLAG_SELF
or AppOpsManager.OP_FLAG_UNTRUSTED_PROXY
or AppOpsManager.OP_FLAG_TRUSTED_PROXY)).isEqualTo(0)
assertThat(attributedPackageOp.getBackgroundAccessCount(
AppOpsManager.OP_FLAG_SELF
or AppOpsManager.OP_FLAG_UNTRUSTED_PROXY
or AppOpsManager.OP_FLAG_TRUSTED_PROXY)).isEqualTo(0)
}
}
private fun assertLastReceiverOps(
op: String,
beginTimeMillis: Long,
endTimeMillis: Long,
attributionSource: AttributionSource,
receiverForeground: Boolean,
accessorTrusted: Boolean,
assertRunning: Boolean,
allPackagesOps: List<AppOpsManager.PackageOps?>
) {
val receiverPackageOps = findPackageOps(
attributionSource.next!!.uid,
attributionSource.next!!.packageName!!,
allPackagesOps)
for (opEntry in receiverPackageOps!!.ops) {
if (op != opEntry.opStr) {
continue
}
val attributedOpEntry = opEntry.attributedOpEntries[
attributionSource.next!!.attributionTag]
val opProxyInfo: AppOpsManager.OpEventProxyInfo?
opProxyInfo = if (accessorTrusted) {
// Received from a trusted accessor. If we got the data from a datasource it
// would blame the accessor in a trusted way
assertLastAccessInRange(attributedOpEntry!!, beginTimeMillis, endTimeMillis,
AppOpsManager.OP_FLAG_TRUSTED_PROXIED, receiverForeground,
assertRunning)
attributedOpEntry.getLastProxyInfo(AppOpsManager.OP_FLAG_TRUSTED_PROXIED)
} else {
// Received from an untrusted accessor
assertLastAccessInRange(attributedOpEntry!!, beginTimeMillis, endTimeMillis,
AppOpsManager.OP_FLAG_UNTRUSTED_PROXIED, receiverForeground,
assertRunning)
attributedOpEntry.getLastProxyInfo(
AppOpsManager.OP_FLAG_UNTRUSTED_PROXIED)
}
assertThat(opProxyInfo!!.uid).isEqualTo(attributionSource.uid)
assertThat(opProxyInfo.packageName).isEqualTo(attributionSource.packageName)
assertThat(opProxyInfo.attributionTag).isEqualTo(attributionSource.attributionTag)
}
}
private fun assertHistoricalReceiverOps(
op: String,
attributionSource: AttributionSource,
receiverForeground: Boolean,
accessorTrusted: Boolean,
assertedAccessCount: Int,
historicalOps: AppOpsManager.HistoricalOps
) {
val accessorPackageOps = findPackageOps(
attributionSource.next!!.uid,
attributionSource.next!!.packageName!!,
historicalOps)
val attributedPackageOps = accessorPackageOps?.getAttributedOps(
attributionSource.next!!.attributionTag!!)
val attributedPackageOp = attributedPackageOps!!.getOp(op)
if (accessorTrusted) {
// Received from a trusted accessor.
assertAccessCount(attributedPackageOp!!, AppOpsManager.OP_FLAG_TRUSTED_PROXIED,
receiverForeground, assertedAccessCount)
} else {
// Received from an untrusted accessor
assertAccessCount(attributedPackageOp!!, AppOpsManager.OP_FLAG_UNTRUSTED_PROXIED,
receiverForeground, assertedAccessCount)
}
}
private fun assertLastAccessInRange(
opEntry: AppOpsManager.AttributedOpEntry,
beginTimeMillis: Long,
endTimeMillis: Long,
assertedFlag: Int,
assertForeground: Boolean,
assertRunning: Boolean
) {
assertThat(opEntry.isRunning).isEqualTo(assertRunning)
assertTimeInRangeIfRequired(opEntry, assertedFlag,
AppOpsManager.OP_FLAG_SELF,
assertForeground, beginTimeMillis, endTimeMillis)
assertTimeInRangeIfRequired(opEntry, assertedFlag,
AppOpsManager.OP_FLAG_TRUSTED_PROXY,
assertForeground, beginTimeMillis, endTimeMillis)
assertTimeInRangeIfRequired(opEntry, assertedFlag,
AppOpsManager.OP_FLAG_UNTRUSTED_PROXY,
assertForeground, beginTimeMillis, endTimeMillis)
assertTimeInRangeIfRequired(opEntry, assertedFlag,
AppOpsManager.OP_FLAG_TRUSTED_PROXIED,
assertForeground, beginTimeMillis, endTimeMillis)
assertTimeInRangeIfRequired(opEntry, assertedFlag,
AppOpsManager.OP_FLAG_UNTRUSTED_PROXIED,
assertForeground, beginTimeMillis, endTimeMillis)
if (assertForeground) {
assertThat(opEntry.getLastAccessBackgroundTime(AppOpsManager.OP_FLAGS_ALL))
.isEqualTo(-1)
} else {
assertThat(opEntry.getLastAccessForegroundTime(AppOpsManager.OP_FLAGS_ALL))
.isEqualTo(-1)
}
}
private fun assertTimeInRangeIfRequired(
opEntry: AppOpsManager.AttributedOpEntry,
assertedFlag: Int,
accessedFlag: Int,
assertForeground: Boolean,
beginTimeMillis: Long,
endTimeMillis: Long
) {
if (assertedFlag != accessedFlag) {
return
}
val accessTime: Long
accessTime = if (assertForeground) {
opEntry.getLastAccessForegroundTime(accessedFlag)
} else {
opEntry.getLastAccessBackgroundTime(accessedFlag)
}
assertThat(accessTime).isAtLeast(beginTimeMillis)
assertThat(accessTime).isAtMost(endTimeMillis)
}
private fun assertAccessCount(
historicalOp: AppOpsManager.HistoricalOp,
assertedFlag: Int,
assertForeground: Boolean,
assertedAccessCount: Int
) {
assertAccessCountIfRequired(historicalOp, AppOpsManager.OP_FLAG_SELF,
assertedFlag, assertForeground, assertedAccessCount)
assertAccessCountIfRequired(historicalOp, AppOpsManager.OP_FLAG_TRUSTED_PROXY,
assertedFlag, assertForeground, assertedAccessCount)
assertAccessCountIfRequired(historicalOp, AppOpsManager.OP_FLAG_UNTRUSTED_PROXY,
assertedFlag, assertForeground, assertedAccessCount)
assertAccessCountIfRequired(historicalOp, AppOpsManager.OP_FLAG_TRUSTED_PROXIED,
assertedFlag, assertForeground, assertedAccessCount)
assertAccessCountIfRequired(historicalOp, AppOpsManager.OP_FLAG_UNTRUSTED_PROXIED,
assertedFlag, assertForeground, assertedAccessCount)
if (assertForeground) {
assertThat(historicalOp.getBackgroundAccessCount(
AppOpsManager.OP_FLAGS_ALL)).isEqualTo(0)
} else {
assertThat(historicalOp.getForegroundAccessCount(
AppOpsManager.OP_FLAGS_ALL)).isEqualTo(0)
}
}
private fun assertAccessCountIfRequired(
historicalOp: AppOpsManager.HistoricalOp,
assertedFlag: Int,
accessedFlag: Int,
assertForeground: Boolean,
assertedAccessCount: Int
) {
if (assertedFlag != accessedFlag) {
return
}
val accessCount: Long
accessCount = if (assertForeground) {
historicalOp.getForegroundAccessCount(accessedFlag)
} else {
historicalOp.getBackgroundAccessCount(accessedFlag)
}
assertThat(accessCount).isEqualTo(assertedAccessCount)
}
private fun findPackageOps(
uid: Int,
packageName: String,
searchedList: List<AppOpsManager.PackageOps?>
): AppOpsManager.PackageOps? {
return searchedList.stream()
.filter { packageOps: AppOpsManager.PackageOps? ->
packageOps!!.uid == uid && packageOps.packageName == packageName
}
.findAny()
.orElse(null)
}
private fun findPackageOps(
uid: Int,
packageName: String,
historicalOps: AppOpsManager.HistoricalOps
): AppOpsManager.HistoricalPackageOps? {
val uidOps = historicalOps.getUidOps(uid)
return uidOps?.getPackageOps(packageName)
}
fun createAttributionContext(
attributionTag: String?,
receiverPackageName: String?,
receiverAttributionTag: String?
): Context {
val attributionParamsBuilder = ContextParams.Builder()
if (attributionTag != null) {
attributionParamsBuilder.setAttributionTag(attributionTag)
}
if (receiverPackageName != null) {
val attributionSourceBuilder = AttributionSource.Builder(
context.packageManager.getPackageUid(receiverPackageName, 0))
attributionSourceBuilder.setPackageName(receiverPackageName)
if (receiverAttributionTag != null) {
attributionSourceBuilder.setAttributionTag(receiverAttributionTag)
}
var receiverAttributionSource = attributionSourceBuilder.build()
SystemUtil.runWithShellPermissionIdentity {
receiverAttributionSource = context.getSystemService(
PermissionManager::class.java)!!.registerAttributionSource(
receiverAttributionSource)
}
attributionParamsBuilder.setNextAttributionSource(receiverAttributionSource)
}
return context.createContext(attributionParamsBuilder.build())
}
fun startBlamedAppActivity(): AttributionSource {
val activityStatedLatch = CountDownLatch(1)
val attributionSourceRef = AtomicReference<AttributionSource>()
val intent = Intent()
intent.setClassName(RECEIVER_PACKAGE_NAME, BRING_TO_FOREGROUND_ACTIVITY)
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP)
intent.putExtra(REMOTE_CALLBACK, RemoteCallback {
attributionSourceRef.set(it?.getParcelable(ATTRIBUTION_SOURCE))
activityStatedLatch.countDown()
})
context.startActivity(intent)
activityStatedLatch.await(ASYNC_OPERATION_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
return attributionSourceRef.get()
}
private fun assumeNotTv() = assumeFalse(isTv)
private fun assumeHasTelephony() = assumeTrue(isTel)
}
}