Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e962cd9f authored by Yu-Ting Tseng's avatar Yu-Ting Tseng Committed by Android (Google) Code Review
Browse files

Merge "Frozen-aware AppOpsService op noted callback" into main

parents 55af57c9 e285b04b
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -238,6 +238,13 @@ flag {
  bug: "372910217"
}

flag {
  name: "use_frozen_aware_remote_callback_list"
  namespace: "permissions"
  description: "Whether to use the new frozen-aware RemoteCallbackList API for op noted callbacks."
  bug: "361157077"
}

flag {
    name: "wallet_role_icon_property_enabled"
    is_exported: true
+15 −0
Original line number Diff line number Diff line
@@ -22,7 +22,9 @@ import android.app.AsyncNotedAppOp
import android.app.Service
import android.app.SyncNotedAppOp
import android.content.Intent
import android.os.Handler
import android.os.IBinder
import android.os.Looper
import android.util.Log
import com.android.frameworks.coretests.aidl.IAppOpsUserClient
import com.android.frameworks.coretests.aidl.IAppOpsUserService
@@ -71,6 +73,7 @@ class AppOpsUserService : Service() {
    override fun onBind(intent: Intent?): IBinder {
        return object : IAppOpsUserService.Stub() {
            private val appOpsManager = getSystemService(AppOpsManager::class.java)!!
            private val handler = Handler(Looper.getMainLooper())

            // Collected note-op calls inside of this process
            private val noted = mutableListOf<Pair<SyncNotedAppOp, Array<StackTraceElement>>>()
@@ -182,6 +185,18 @@ class AppOpsUserService : Service() {
                }
            }

            override fun callFreezeAndNoteSyncOp(client: IAppOpsUserClient) {
                handler.post {
                    client.freezeAndNoteSyncOp()
                }
            }

            override fun assertEmptyAsyncNoted() {
                forwardThrowableFrom {
                    assertThat(asyncNoted).isEmpty()
                }
            }

            override fun callApiThatNotesAsyncOpNativelyAndCheckCustomMessage(
                client: IAppOpsUserClient
            ) {
+1 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ interface IAppOpsUserClient {
    void noteSyncOpNative();
    void noteNonPermissionSyncOpNative();
    oneway void noteSyncOpOnewayNative();
    void freezeAndNoteSyncOp();
    void noteSyncOpOtherUidNative();
    void noteAsyncOpNative();
    void noteAsyncOpNativeWithCustomMessage();
+2 −0
Original line number Diff line number Diff line
@@ -25,4 +25,6 @@ interface IAppOpsUserService {
    void callApiThatNotesSyncOpOtherUidNativelyAndCheckLog(in IAppOpsUserClient client);
    void callApiThatNotesAsyncOpNativelyAndCheckCustomMessage(in IAppOpsUserClient client);
    void callApiThatNotesAsyncOpNativelyAndCheckLog(in IAppOpsUserClient client);
    void callFreezeAndNoteSyncOp(in IAppOpsUserClient client);
    void assertEmptyAsyncNoted();
}
+72 −0
Original line number Diff line number Diff line
@@ -33,6 +33,9 @@ import android.os.IBinder
import android.os.Looper
import android.os.Process
import android.platform.test.annotations.AppModeFull
import android.platform.test.annotations.RequiresFlagsEnabled
import android.platform.test.flag.junit.CheckFlagsRule
import android.platform.test.flag.junit.DeviceFlagsValueProvider
import android.util.Log
import androidx.test.platform.app.InstrumentationRegistry
import com.android.frameworks.coretests.aidl.IAppOpsUserClient
@@ -41,9 +44,15 @@ import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Assert.fail
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import java.util.concurrent.CompletableFuture
import java.util.concurrent.LinkedBlockingQueue
import java.util.concurrent.TimeUnit.MILLISECONDS
import java.util.concurrent.TimeUnit
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.TimeSource

private const val LOG_TAG = "AppOpsLoggingTest"

@@ -71,8 +80,11 @@ class AppOpsLoggingTest {

    private var wasLocationEnabled = false

    @get:Rule val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()

    private lateinit var testService: IAppOpsUserService
    private lateinit var serviceConnection: ServiceConnection
    private lateinit var freezingTestCompletion: CompletableFuture<Unit>

    // Collected note-op calls inside of this process
    private val noted = mutableListOf<Pair<SyncNotedAppOp, Array<StackTraceElement>>>()
@@ -123,6 +135,7 @@ class AppOpsLoggingTest {

        context.bindService(serviceIntent, serviceConnection, BIND_AUTO_CREATE)
        testService = newService.get(TIMEOUT_MILLIS, MILLISECONDS)
        freezingTestCompletion = CompletableFuture<Unit>()
    }

    private fun clearCollectedNotedOps() {
@@ -253,6 +266,26 @@ class AppOpsLoggingTest {
        }
    }

    @Test
    @RequiresFlagsEnabled(android.os.Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK,
            android.permission.flags.Flags.FLAG_USE_FROZEN_AWARE_REMOTE_CALLBACK_LIST)
    fun dropAsyncOpNotedWhenFrozen() {
        // Here's what the test does:
        // 1. AppOpsLoggingTest calls AppOpsUserService
        // 2. AppOpsUserService calls freezeAndNoteSyncOp in AppOpsLoggingTest
        // 3. freezeAndNoteSyncOp freezes AppOpsUserService
        // 4. freezeAndNoteSyncOp calls nativeNoteOp which leads to an async op noted callback
        // 5. AppOpsService is expected to drop the callback (via RemoteCallbackList) since
        //    AppOpsUserService is frozen
        // 6. freezeAndNoteSyncOp unfreezes AppOpsUserService
        // 7. AppOpsLoggingTest calls AppOpsUserService.assertEmptyAsyncNoted
        rethrowThrowableFrom {
            testService.callFreezeAndNoteSyncOp(AppOpsUserClient(context))
            freezingTestCompletion.get()
            testService.assertEmptyAsyncNoted()
        }
    }

    @After
    fun removeNotedAppOpsCollector() {
        appOpsManager.setOnOpNotedCallback(null, null)
@@ -263,6 +296,20 @@ class AppOpsLoggingTest {
        context.unbindService(serviceConnection)
    }

    fun <T> waitForState(queue: LinkedBlockingQueue<T>, state: T, duration: Duration): T? {
        val timeSource = TimeSource.Monotonic
        val start = timeSource.markNow()
        var remaining = duration
        while (remaining.inWholeMilliseconds > 0) {
            val v = queue.poll(remaining.inWholeMilliseconds, TimeUnit.MILLISECONDS)
            if (v == state) {
                return v
            }
            remaining -= timeSource.markNow() - start
        }
        return null
    }

    private inner class AppOpsUserClient(
        context: Context
    ) : IAppOpsUserClient.Stub() {
@@ -285,6 +332,31 @@ class AppOpsLoggingTest {
            nativeNoteOp(strOpToOp(OPSTR_COARSE_LOCATION), Binder.getCallingUid(), TEST_SERVICE_PKG)
        }

        override fun freezeAndNoteSyncOp() {
            handler.post {
                var stateChanges = LinkedBlockingQueue<Int>()
                // Leave some time for any pending binder transactions to complete.
                //
                // TODO(327047060) Remove this sleep and instead make am freeze wait for binder
                // transactions to complete
                Thread.sleep(1000)
                testService.asBinder().addFrozenStateChangeCallback {
                    _, state -> stateChanges.put(state)
                }
                InstrumentationRegistry.getInstrumentation().uiAutomation
                    .executeShellCommand("am freeze $TEST_SERVICE_PKG")
                waitForState(stateChanges, IBinder.FrozenStateChangeCallback.STATE_FROZEN,
                    1000.milliseconds)
                nativeNoteOp(strOpToOp(OPSTR_COARSE_LOCATION), Binder.getCallingUid(),
                    TEST_SERVICE_PKG)
                InstrumentationRegistry.getInstrumentation().uiAutomation
                    .executeShellCommand("am unfreeze $TEST_SERVICE_PKG")
                waitForState(stateChanges, IBinder.FrozenStateChangeCallback.STATE_UNFROZEN,
                    1000.milliseconds)
                freezingTestCompletion.complete(Unit)
            }
        }

        override fun noteSyncOpOtherUidNative() {
            nativeNoteOp(strOpToOp(OPSTR_COARSE_LOCATION), myUid, myPackage)
        }
Loading