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

Commit c7e1fa6a authored by Himanshu Arora's avatar Himanshu Arora
Browse files

Add SDK version checks for Trash feature

The Trash feature relies on DocumentsContract APIs introduced after Baklava and is not backward compatible. These checks prevent runtime errors and test failures.

Bug: 442400425
Test: atest DocumentsUIGoogleTests:com.android.documentsui.services.TrashJobTest
Flag: com.android.documentsui.flags.enable_trash_flow_ro
Change-Id: I369ba0971236613973761719a960d1dcab9598b2
parent 4b6ec46c
Loading
Loading
Loading
Loading
+31 −28
Original line number Diff line number Diff line
@@ -21,12 +21,9 @@ import android.util.Log
import com.android.documentsui.flags.Flags
import com.android.modules.utils.build.SdkLevel

/**
 * Wrap aconfig generated flags to allow us to override flags in tests.
 */
class FlagUtils private constructor(
    private val overrides: MutableMap<String, Boolean> = mutableMapOf()
) {
/** Wrap aconfig generated flags to allow us to override flags in tests. */
class FlagUtils
private constructor(private val overrides: MutableMap<String, Boolean> = mutableMapOf()) {
    companion object {
        private const val TAG = "FlagUtils"
        @Volatile private var instance: FlagUtils = FlagUtils()
@@ -69,27 +66,26 @@ class FlagUtils private constructor(

        @JvmStatic
        fun isSearchV2Enabled(): Boolean {
            val flag = getInstance().overrides.getOrDefault(
                Flags.FLAG_USE_SEARCH_V2_READ_ONLY,
                Flags.useSearchV2ReadOnly()
            )
            val flag =
                getInstance()
                    .overrides
                    .getOrDefault(Flags.FLAG_USE_SEARCH_V2_READ_ONLY, Flags.useSearchV2ReadOnly())
            return flag && isUseMaterial3FlagEnabled()
        }

        @JvmStatic
        fun isDesktopFileHandlingFlagEnabled(): Boolean {
            return getInstance().overrides.getOrDefault(
                Flags.FLAG_DESKTOP_FILE_HANDLING_RO,
                Flags.desktopFileHandlingRo()
            )
            return getInstance()
                .overrides
                .getOrDefault(Flags.FLAG_DESKTOP_FILE_HANDLING_RO, Flags.desktopFileHandlingRo())
        }

        @JvmStatic
        fun isDesktopUxPhase2FlagEnabled(): Boolean {
            val flag = getInstance().overrides.getOrDefault(
                Flags.FLAG_DESKTOP_UX_PHASE_2_RO,
                Flags.desktopUxPhase2Ro()
            )
            val flag =
                getInstance()
                    .overrides
                    .getOrDefault(Flags.FLAG_DESKTOP_UX_PHASE_2_RO, Flags.desktopUxPhase2Ro())
            return flag && isUseMaterial3FlagEnabled()
        }

@@ -102,10 +98,10 @@ class FlagUtils private constructor(

        @JvmStatic
        fun isVisualSignalsFlagEnabled(): Boolean {
            val flag = getInstance().overrides.getOrDefault(
                Flags.FLAG_VISUAL_SIGNALS_RO,
                Flags.visualSignalsRo()
            )
            val flag =
                getInstance()
                    .overrides
                    .getOrDefault(Flags.FLAG_VISUAL_SIGNALS_RO, Flags.visualSignalsRo())
            return flag && isUseMaterial3FlagEnabled()
        }

@@ -118,17 +114,24 @@ class FlagUtils private constructor(

        @JvmStatic
        fun isTrashFlowEnabled(): Boolean {
            if (!SdkLevel.isAtLeastB()) {
            // Check if the platform SDK is newer than Android Baklava (SDK 36).
            // The Trash feature relies on DocumentsContract APIs introduced in the
            // Android release after Baklava.
            // This specific Trash feature is NOT backward compatible with platforms
            // at or below Baklava because the required APIs are missing.
            // This check ensures the feature is only considered enabled on
            // supported platform versions, preventing runtime errors if the module
            // runs on an older base OS.
            if (!VersionUtils.isGreaterThanB()) {
                return false
            }
            // If API flag is not enabled, then trash flow will be disabled
            if (!enableDocumentsTrashApi()) {
                return false
            }
            return getInstance().overrides.getOrDefault(
                Flags.FLAG_ENABLE_TRASH_FLOW_RO,
                Flags.enableTrashFlowRo()
            )
            return getInstance()
                .overrides
                .getOrDefault(Flags.FLAG_ENABLE_TRASH_FLOW_RO, Flags.enableTrashFlowRo())
        }

        @JvmStatic
+9 −0
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.documentsui.util;

import android.os.Build;

import com.android.modules.utils.build.SdkLevel;

/**
@@ -40,4 +42,11 @@ public class VersionUtils {
    public static boolean isAtLeastS() {
        return SdkLevel.isAtLeastS();
    }

    /**
     * Returns whether the device is running on a version newer than Android BAKLAVA.
     */
    public static boolean isGreaterThanB() {
        return Build.VERSION.SDK_INT > Build.VERSION_CODES.BAKLAVA;
    }
}
+4 −2
Original line number Diff line number Diff line
@@ -86,9 +86,11 @@ public abstract class AbstractJobTest<T extends Job> {

    @After
    public void tearDown() throws Exception {
        if (mDocs != null) {
            resetStorage();
            mDocs.cleanUp();
        }
    }

    private void resetStorage() throws RemoteException {
        mDocs.clear(null, null);
+15 −3
Original line number Diff line number Diff line
@@ -20,7 +20,6 @@ import android.app.Notification.CATEGORY_ERROR
import android.app.Notification.EXTRA_TEXT
import android.app.Notification.EXTRA_TITLE
import android.net.Uri
import android.os.Build
import android.platform.test.annotations.EnableFlags
import android.platform.test.annotations.RequiresFlagsEnabled
import android.platform.test.flag.junit.CheckFlagsRule
@@ -28,12 +27,13 @@ import android.platform.test.flag.junit.DeviceFlagsValueProvider
import android.provider.DocumentsContract.buildDocumentUri
import android.provider.Flags.FLAG_ENABLE_DOCUMENTS_TRASH_API
import androidx.test.filters.MediumTest
import androidx.test.filters.SdkSuppress
import com.android.documentsui.TrashDocumentHelper
import com.android.documentsui.flags.Flags
import com.android.documentsui.rules.OverrideFlagsRule
import com.android.documentsui.services.FileOperationService.OPERATION_RESTORE
import com.android.documentsui.util.VersionUtils
import com.google.common.truth.Truth.assertThat
import org.junit.Assume.assumeTrue
import org.junit.Rule
import org.junit.Test

@@ -52,13 +52,25 @@ import org.junit.Test
 * directories (<parent_1>, <parent_2>) inside ".trash-storage" are then removed.
 */
@MediumTest
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava")
@RequiresFlagsEnabled(FLAG_ENABLE_DOCUMENTS_TRASH_API)
internal class RestoreJobTest : AbstractJobTest<TrashJob>() {
    @get:Rule val setFlags = OverrideFlagsRule()

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

    override fun setUp() {
        // Skip test if the platform SDK is not newer than Android Baklava (SDK 36).
        // The Trash feature under test relies on DocumentsContract APIs introduced in the
        // Android release after Baklava (SDK 36).
        // As DocumentsUI is a Mainline module, it's subject to MTS testing, which runs on
        // older Android base builds to verify backward compatibility. However, this specific
        // Trash feature lacks backward compatibility with platforms at or below Baklava.
        // This assumption prevents failures when the test runs on an older base OS
        // without the necessary APIs.
        assumeTrue(VersionUtils.isGreaterThanB())
        super.setUp()
    }

    /**
     * Tests the restoration of a single file. Verifies that the file is moved to its original path
     * and that the parent directories within the trash are removed.
+16 −7
Original line number Diff line number Diff line
@@ -20,7 +20,6 @@ import android.app.Notification.CATEGORY_ERROR
import android.app.Notification.EXTRA_TEXT
import android.app.Notification.EXTRA_TITLE
import android.net.Uri
import android.os.Build
import android.platform.test.annotations.EnableFlags
import android.platform.test.annotations.RequiresFlagsEnabled
import android.platform.test.flag.junit.CheckFlagsRule
@@ -28,26 +27,39 @@ import android.platform.test.flag.junit.DeviceFlagsValueProvider
import android.provider.DocumentsContract.buildDocumentUri
import android.provider.Flags.FLAG_ENABLE_DOCUMENTS_TRASH_API
import androidx.test.filters.MediumTest
import androidx.test.filters.SdkSuppress
import com.android.documentsui.TrashDocumentHelper
import com.android.documentsui.base.DocumentInfo
import com.android.documentsui.flags.Flags
import com.android.documentsui.rules.OverrideFlagsRule
import com.android.documentsui.services.FileOperationService.OPERATION_TRASH
import com.android.documentsui.util.VersionUtils
import com.google.common.truth.Truth.assertThat
import org.junit.Assume.assumeTrue
import org.junit.Rule
import org.junit.Test

/** Tests TrashJob. */
@MediumTest
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava")
@RequiresFlagsEnabled(FLAG_ENABLE_DOCUMENTS_TRASH_API)
internal class TrashJobTest : AbstractJobTest<TrashJob>() {
    @get:Rule val setFlags = OverrideFlagsRule()

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

    override fun setUp() {
        // Skip test if the platform SDK is not newer than Android Baklava (SDK 36).
        // The Trash feature under test relies on DocumentsContract APIs introduced in the
        // Android release after Baklava (SDK 36).
        // As DocumentsUI is a Mainline module, it's subject to MTS testing, which runs on
        // older Android base builds to verify backward compatibility. However, this specific
        // Trash feature lacks backward compatibility with platforms at or below Baklava.
        // This assumption prevents failures when the test runs on an older base OS
        // without the necessary APIs.
        assumeTrue(VersionUtils.isGreaterThanB())
        super.setUp()
    }

    @Test
    @RequiresFlagsEnabled(FLAG_ENABLE_DOCUMENTS_TRASH_API)
    @EnableFlags(Flags.FLAG_ENABLE_TRASH_FLOW_RO)
    fun testTrashSingleFile() {
        val testDir1 = mDocs.createFolder(mSrcRoot, "dir1")
@@ -92,7 +104,6 @@ internal class TrashJobTest : AbstractJobTest<TrashJob>() {
    }

    @Test
    @RequiresFlagsEnabled(FLAG_ENABLE_DOCUMENTS_TRASH_API)
    @EnableFlags(Flags.FLAG_ENABLE_TRASH_FLOW_RO)
    fun testTrashMultipleFile() {
        val testDir1 = mDocs.createFolder(mSrcRoot, "dir1")
@@ -140,7 +151,6 @@ internal class TrashJobTest : AbstractJobTest<TrashJob>() {
    }

    @Test
    @RequiresFlagsEnabled(FLAG_ENABLE_DOCUMENTS_TRASH_API)
    @EnableFlags(Flags.FLAG_ENABLE_TRASH_FLOW_RO)
    fun testTrashFolder() {
        val testDir1 = mDocs.createFolder(mSrcRoot, "dir1")
@@ -203,7 +213,6 @@ internal class TrashJobTest : AbstractJobTest<TrashJob>() {
    }

    @Test
    @RequiresFlagsEnabled(FLAG_ENABLE_DOCUMENTS_TRASH_API)
    @EnableFlags(Flags.FLAG_ENABLE_TRASH_FLOW_RO)
    fun testTrashFailedNoFileFound() {
        // create a document in mDestRoot, trashDocument only working for mSrcRoot,
Loading