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

Commit a42119c6 authored by Chaohui Wang's avatar Chaohui Wang Committed by Android (Google) Code Review
Browse files

Merge "Migrate latest changes in "Alarms and Reminders"" into udc-dev

parents e1ae1c65 f6caf243
Loading
Loading
Loading
Loading
+34 −7
Original line number Diff line number Diff line
@@ -21,12 +21,14 @@ import android.app.AlarmManager
import android.app.compat.CompatChanges
import android.content.Context
import android.content.pm.ApplicationInfo
import android.os.PowerExemptionManager
import androidx.compose.runtime.Composable
import androidx.compose.runtime.livedata.observeAsState
import com.android.settings.R
import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spaprivileged.model.app.AppRecord
import com.android.settingslib.spaprivileged.model.app.IPackageManagers
import com.android.settingslib.spaprivileged.model.app.PackageManagers
import com.android.settingslib.spaprivileged.model.app.PackageManagers.hasRequestPermission
import com.android.settingslib.spaprivileged.model.app.userHandle
import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListModel
import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider
@@ -41,12 +43,14 @@ object AlarmsAndRemindersAppListProvider : TogglePermissionAppListProvider {

data class AlarmsAndRemindersAppRecord(
    override val app: ApplicationInfo,
    val isTrumped: Boolean,
    val isChangeable: Boolean,
    var controller: AlarmsAndRemindersController,
) : AppRecord

class AlarmsAndRemindersAppListModel(
    private val context: Context,
    private val packageManagers: IPackageManagers = PackageManagers,
) : TogglePermissionAppListModel<AlarmsAndRemindersAppRecord> {
    override val pageTitleResId = R.string.alarms_and_reminders_title
    override val switchTitleResId = R.string.alarms_and_reminders_switch_title
@@ -61,8 +65,9 @@ class AlarmsAndRemindersAppListModel(
            }
        }

    override fun transformItem(app: ApplicationInfo) =
    override fun transformItem(app: ApplicationInfo) = with(packageManagers) {
        createRecord(app = app, hasRequestPermission = app.hasRequestPermission(PERMISSION))
    }

    override fun filter(
        userIdFlow: Flow<Int>,
@@ -73,7 +78,8 @@ class AlarmsAndRemindersAppListModel(

    @Composable
    override fun isAllowed(record: AlarmsAndRemindersAppRecord) =
        record.controller.isAllowed.observeAsState()
        if (record.isTrumped) stateOf(true)
        else record.controller.isAllowed.observeAsState()

    override fun isChangeable(record: AlarmsAndRemindersAppRecord) = record.isChangeable

@@ -81,17 +87,38 @@ class AlarmsAndRemindersAppListModel(
        record.controller.setAllowed(newAllowed)
    }

    private fun createRecord(app: ApplicationInfo, hasRequestPermission: Boolean) =
        AlarmsAndRemindersAppRecord(
    private fun createRecord(
        app: ApplicationInfo,
        hasRequestPermission: Boolean,
    ): AlarmsAndRemindersAppRecord {
        val hasRequestSeaPermission = hasRequestPermission && app.isSeaEnabled()
        val isTrumped = hasRequestSeaPermission && app.isTrumped()
        return AlarmsAndRemindersAppRecord(
            app = app,
            isChangeable = hasRequestPermission && app.isChangeEnabled(),
            isTrumped = isTrumped,
            isChangeable = hasRequestPermission && !isTrumped,
            controller = AlarmsAndRemindersController(context, app),
        )
    }

    /**
     * If trumped, this app will be treated as allowed, and the toggle is not changeable by user.
     */
    private fun ApplicationInfo.isTrumped(): Boolean = with(packageManagers) {
        val hasRequestUseExactAlarm = hasRequestPermission(Manifest.permission.USE_EXACT_ALARM) &&
            CompatChanges.isChangeEnabled(
                AlarmManager.ENABLE_USE_EXACT_ALARM, packageName, userHandle,
            )
        val isPowerAllowListed = context.getSystemService(PowerExemptionManager::class.java)
            ?.isAllowListed(packageName, true) ?: false
        return hasRequestUseExactAlarm || isPowerAllowListed
    }

    companion object {
        private const val PERMISSION: String = Manifest.permission.SCHEDULE_EXACT_ALARM

        private fun ApplicationInfo.isChangeEnabled(): Boolean =
        /** Checks whether [Manifest.permission.SCHEDULE_EXACT_ALARM] is enabled. */
        private fun ApplicationInfo.isSeaEnabled(): Boolean =
            CompatChanges.isChangeEnabled(
                AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, packageName, userHandle,
            )
+133 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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 com.android.settings.spa.app.specialaccess

import android.Manifest
import android.app.compat.CompatChanges
import android.content.Context
import android.content.pm.ApplicationInfo
import android.os.PowerExemptionManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.settingslib.spaprivileged.model.app.IPackageManagers
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.Mockito.anyLong
import org.mockito.Mockito.anyString
import org.mockito.MockitoSession
import org.mockito.Spy
import org.mockito.quality.Strictness
import org.mockito.Mockito.`when` as whenever

@RunWith(AndroidJUnit4::class)
class AlarmsAndRemindersAppListTest {
    private lateinit var mockSession: MockitoSession

    @Spy
    private val context: Context = ApplicationProvider.getApplicationContext()

    @Mock
    private lateinit var powerExemptionManager: PowerExemptionManager

    @Mock
    private lateinit var packageManagers: IPackageManagers

    private lateinit var listModel: AlarmsAndRemindersAppListModel

    @Before
    fun setUp() {
        mockSession = ExtendedMockito.mockitoSession()
            .initMocks(this)
            .mockStatic(CompatChanges::class.java)
            .strictness(Strictness.LENIENT)
            .startMocking()
        whenever(CompatChanges.isChangeEnabled(anyLong(), anyString(), any())).thenReturn(true)
        whenever(context.getSystemService(PowerExemptionManager::class.java))
            .thenReturn(powerExemptionManager)
        with(packageManagers) {
            whenever(APP.hasRequestPermission(Manifest.permission.SCHEDULE_EXACT_ALARM))
                .thenReturn(true)
        }
        listModel = AlarmsAndRemindersAppListModel(context, packageManagers)
    }

    @After
    fun tearDown() {
        mockSession.finishMocking()
    }

    @Test
    fun transformItem_recordHasCorrectApp() {
        val record = listModel.transformItem(APP)

        assertThat(record.app).isSameInstanceAs(APP)
    }

    @Test
    fun transformItem_whenNotRequestScheduleExactAlarm_recordHasCorrectState() {
        with(packageManagers) {
            whenever(APP.hasRequestPermission(Manifest.permission.SCHEDULE_EXACT_ALARM))
                .thenReturn(false)
        }
        val record = listModel.transformItem(APP)

        assertThat(record.isTrumped).isFalse()
        assertThat(record.isChangeable).isFalse()
    }

    @Test
    fun transformItem_whenRequestUseExactAlarm_recordHasCorrectState() {
        with(packageManagers) {
            whenever(APP.hasRequestPermission(Manifest.permission.USE_EXACT_ALARM))
                .thenReturn(true)
        }
        val record = listModel.transformItem(APP)

        assertThat(record.isTrumped).isTrue()
        assertThat(record.isChangeable).isFalse()
    }

    @Test
    fun transformItem_whenPowerAllowListed_recordHasCorrectState() {
        whenever(powerExemptionManager.isAllowListed(PACKAGE_NAME, true)).thenReturn(true)
        val record = listModel.transformItem(APP)

        assertThat(record.isTrumped).isTrue()
        assertThat(record.isChangeable).isFalse()
    }

    @Test
    fun transformItem_whenNotTrumped_recordHasCorrectState() {
        val record = listModel.transformItem(APP)

        assertThat(record.isTrumped).isFalse()
        assertThat(record.isChangeable).isTrue()
    }

    private companion object {
        const val PACKAGE_NAME = "package.name"
        val APP = ApplicationInfo().apply {
            packageName = PACKAGE_NAME
        }
    }
}
 No newline at end of file