xref: /aosp_15_r20/cts/tests/tests/appop2/src/android/app/appops2/cts/AppOpsLoggingTest.kt (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)
1 /*
<lambda>null2  * Copyright (C) 2020 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 android.app.appops2.cts
18 
19 import android.app.AppOpsManager
20 import android.app.AppOpsManager.OnOpNotedCallback
21 import android.app.AppOpsManager.permissionToOp
22 import android.app.AsyncNotedAppOp
23 import android.app.PendingIntent
24 import android.app.SyncNotedAppOp
25 import android.app.appops.cts.eventually
26 import android.content.BroadcastReceiver
27 import android.content.Context
28 import android.content.Intent
29 import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK
30 import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
31 import android.content.IntentFilter
32 import android.content.pm.PackageInstaller.EXTRA_STATUS
33 import android.content.pm.PackageInstaller.STATUS_FAILURE_INVALID
34 import android.content.pm.PackageInstaller.STATUS_PENDING_USER_ACTION
35 import android.content.pm.PackageInstaller.SessionParams
36 import android.content.pm.PackageInstaller.SessionParams.MODE_FULL_INSTALL
37 import android.platform.test.annotations.AppModeFull
38 import androidx.test.platform.app.InstrumentationRegistry
39 import com.google.common.truth.Truth.assertThat
40 import org.junit.After
41 import org.junit.Before
42 import org.junit.Test
43 import java.io.File
44 import java.util.concurrent.Executor
45 
46 private const val TEST_ATTRIBUTION_TAG = "testAttribution"
47 
48 @AppModeFull(reason = "Test relies on other app to connect to. Instant apps can't see other apps")
49 class AppOpsLoggingTest {
50     private val context = InstrumentationRegistry.getInstrumentation().targetContext
51     private val appOpsManager = context.getSystemService(AppOpsManager::class.java)!!
52 
53     // Collected note-op calls inside of this process
54     private val noted = mutableListOf<Pair<SyncNotedAppOp, Array<StackTraceElement>>>()
55     private val selfNoted = mutableListOf<Pair<SyncNotedAppOp, Array<StackTraceElement>>>()
56     private val asyncNoted = mutableListOf<AsyncNotedAppOp>()
57 
58     @Before
59     fun setNotedAppOpsCollectorAndClearCollectedNoteOps() {
60         setNotedAppOpsCollector()
61         clearCollectedNotedOps()
62     }
63 
64     private fun clearCollectedNotedOps() {
65         noted.clear()
66         selfNoted.clear()
67         asyncNoted.clear()
68     }
69 
70     private fun setNotedAppOpsCollector() {
71         appOpsManager.setOnOpNotedCallback(Executor { it.run() },
72                 object : OnOpNotedCallback() {
73                     override fun onNoted(op: SyncNotedAppOp) {
74                         noted.add(op to Throwable().stackTrace)
75                     }
76 
77                     override fun onSelfNoted(op: SyncNotedAppOp) {
78                         selfNoted.add(op to Throwable().stackTrace)
79                     }
80 
81                     override fun onAsyncNoted(asyncOp: AsyncNotedAppOp) {
82                         asyncNoted.add(asyncOp)
83                     }
84                 })
85     }
86 
87     /**
88      * Realistic end-to-end test for requesting to install a package
89      */
90     @Test
91     fun requestInstall() {
92         val pi = context.createAttributionContext(TEST_ATTRIBUTION_TAG).packageManager
93                 .packageInstaller
94         val sessionId = pi.createSession(SessionParams(MODE_FULL_INSTALL))
95 
96         val session = pi.openSession(sessionId)
97         try {
98             // Write apk data to session
99             File("/data/local/tmp/cts/appops2/CtsAppToBlame1.apk")
100                     .inputStream().use { fileOnDisk ->
101                         session.openWrite("base.apk", 0, -1).use { sessionFile ->
102                             fileOnDisk.copyTo(sessionFile)
103                         }
104                     }
105 
106             val installAction = context.packageName + ".install_cb"
107             context.registerReceiver(object : BroadcastReceiver() {
108                 override fun onReceive(ignored: Context?, intent: Intent) {
109                     if (intent.getIntExtra(EXTRA_STATUS, STATUS_FAILURE_INVALID)
110                             != STATUS_PENDING_USER_ACTION) {
111                         return
112                     }
113 
114                     // Start package install request UI (should trigger REQUEST_INSTALL_PACKAGES)
115                     val activityIntent = intent.getParcelableExtra<Intent>(Intent.EXTRA_INTENT)
116                     activityIntent!!.addFlags(
117                             FLAG_ACTIVITY_CLEAR_TASK or FLAG_ACTIVITY_NEW_TASK)
118                     context.startActivity(activityIntent)
119                 }
120             }, IntentFilter(installAction), Context.RECEIVER_EXPORTED)
121 
122             // Commit session (should trigger installAction receiver)
123             session.commit(PendingIntent.getBroadcast(context, 0,
124                     Intent(installAction).setPackage(context.packageName),
125                     PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE).intentSender)
126 
127             eventually {
128                 assertThat(asyncNoted[0].op).isEqualTo(
129                         permissionToOp(android.Manifest.permission.REQUEST_INSTALL_PACKAGES))
130                 assertThat(asyncNoted[0].attributionTag).isEqualTo(TEST_ATTRIBUTION_TAG)
131             }
132         } finally {
133             session.abandon()
134         }
135     }
136 
137     @After
138     fun removeNotedAppOpsCollector() {
139         appOpsManager.setOnOpNotedCallback(null, null)
140     }
141 }
142