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

Commit 360220c2 authored by Chaitanya Cheemala (xWF)'s avatar Chaitanya Cheemala (xWF) Committed by Android (Google) Code Review
Browse files

Merge "Revert "Integrate Settings with Preference Service"" into main

parents c6636f9a b2986f77
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -132,7 +132,6 @@ android_library {
    ],
    flags_packages: [
        "aconfig_settings_flags",
        "aconfig_settingslib_flags",
        "android.app.flags-aconfig",
        "android.provider.flags-aconfig",
        "android.security.flags-aconfig",
+0 −11
Original line number Diff line number Diff line
@@ -5412,17 +5412,6 @@
            </intent-filter>
        </service>

        <!-- Service to expose Preference Metadata and Get/Set functionality -->
        <service
            android:name=".service.PreferenceService"
            android:exported="true"
            android:featureFlag="com.android.settingslib.flags.settings_catalyst"
            android:permission="android.permission.READ_SYSTEM_PREFERENCES">
            <intent-filter>
                <action android:name="android.service.settings.preferences.action.PREFERENCE_SERVICE" />
            </intent-filter>
        </service>

        <receiver android:name="com.android.settings.connecteddevice.audiosharing.AudioSharingReceiver"
            android:exported="false">
            <intent-filter>
+0 −92
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.service

import android.os.Binder
import android.os.OutcomeReceiver
import android.os.Process
import android.service.settings.preferences.GetValueRequest
import android.service.settings.preferences.GetValueResult
import android.service.settings.preferences.MetadataRequest
import android.service.settings.preferences.MetadataResult
import android.service.settings.preferences.SetValueRequest
import android.service.settings.preferences.SetValueResult
import android.service.settings.preferences.SettingsPreferenceService
import com.android.settingslib.graph.PreferenceGetterApiHandler
import com.android.settingslib.graph.PreferenceSetterApiHandler
import com.android.settingslib.ipc.ApiPermissionChecker
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import java.lang.Exception

class PreferenceService : SettingsPreferenceService() {

    private val scope = CoroutineScope(Job() + Dispatchers.Main)

    private val getApiHandler = PreferenceGetterApiHandler(1, ApiPermissionChecker.alwaysAllow())
    private val setApiHandler = PreferenceSetterApiHandler(2, ApiPermissionChecker.alwaysAllow())

    override fun onGetAllPreferenceMetadata(
        request: MetadataRequest,
        callback: OutcomeReceiver<MetadataResult, Exception>
    ) {
        // TODO(379750656): Update graph API to be usable outside SettingsLib
        callback.onError(UnsupportedOperationException("Not yet supported"))
    }

    override fun onGetPreferenceValue(
        request: GetValueRequest,
        callback: OutcomeReceiver<GetValueResult, Exception>
    ) {
        scope.launch(Dispatchers.IO) {
            val apiRequest = transformFrameworkGetValueRequest(request)
            val response = getApiHandler.invoke(application, Process.myUid(),
                Binder.getCallingPid(), apiRequest)
            val result = transformCatalystGetValueResponse(
                this@PreferenceService,
                request,
                response
            )
            if (result == null) {
                callback.onError(IllegalStateException("No response"))
            } else {
                callback.onResult(result)
            }
        }
    }

    override fun onSetPreferenceValue(
        request: SetValueRequest,
        callback: OutcomeReceiver<SetValueResult, Exception>
    ) {
        scope.launch(Dispatchers.IO) {
            val apiRequest = transformFrameworkSetValueRequest(request)
            if (apiRequest == null) {
                callback.onResult(
                    SetValueResult.Builder(SetValueResult.RESULT_INVALID_REQUEST).build()
                )
            } else {
                val response = setApiHandler.invoke(application, Process.myUid(),
                    Binder.getCallingPid(), apiRequest)

                callback.onResult(transformCatalystSetValueResponse(response))
            }
        }
    }
}
+0 −156
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.service

import android.content.Context
import android.service.settings.preferences.GetValueRequest
import android.service.settings.preferences.GetValueResult
import android.service.settings.preferences.SetValueRequest
import android.service.settings.preferences.SetValueResult
import android.service.settings.preferences.SettingsPreferenceMetadata
import android.service.settings.preferences.SettingsPreferenceValue
import com.android.settingslib.graph.PreferenceCoordinate
import com.android.settingslib.graph.PreferenceGetterErrorCode
import com.android.settingslib.graph.PreferenceGetterFlags
import com.android.settingslib.graph.PreferenceGetterRequest
import com.android.settingslib.graph.PreferenceGetterResponse
import com.android.settingslib.graph.PreferenceSetterRequest
import com.android.settingslib.graph.PreferenceSetterResult
import com.android.settingslib.graph.preferenceValueProto
import com.android.settingslib.graph.proto.PreferenceProto
import com.android.settingslib.graph.proto.PreferenceValueProto
import com.android.settingslib.graph.getText
import com.android.settingslib.graph.toIntent
import com.android.settingslib.metadata.SensitivityLevel

/** Translate Framework GET VALUE request to Catalyst GET VALUE request */
fun transformFrameworkGetValueRequest(
    request: GetValueRequest,
    flags: Int = PreferenceGetterFlags.ALL
): PreferenceGetterRequest {
    val coord = PreferenceCoordinate(request.screenKey, request.preferenceKey)
    return PreferenceGetterRequest(
        arrayOf(coord),
        flags
    )
}

/** Translate Catalyst GET VALUE result to Framework GET VALUE result */
fun transformCatalystGetValueResponse(
    context: Context,
    request: GetValueRequest,
    response: PreferenceGetterResponse
): GetValueResult? {
    val coord = PreferenceCoordinate(request.screenKey, request.preferenceKey)
    val errorResponse = response.errors[coord]
    val valueResponse = response.preferences[coord]
    when {
        errorResponse != null -> {
            val errorCode = when (errorResponse) {
                PreferenceGetterErrorCode.NOT_FOUND -> GetValueResult.RESULT_UNSUPPORTED
                PreferenceGetterErrorCode.DISALLOW -> GetValueResult.RESULT_DISALLOW
                else -> GetValueResult.RESULT_INTERNAL_ERROR
            }
            return GetValueResult.Builder(errorCode).build()
        }
        valueResponse != null -> {
            val resultBuilder = GetValueResult.Builder(GetValueResult.RESULT_OK)
            resultBuilder.setMetadata(valueResponse.toMetadata(context, coord.screenKey))
            val prefValue = valueResponse.value
            when (prefValue.valueCase.number) {
                PreferenceValueProto.BOOLEAN_VALUE_FIELD_NUMBER -> {
                    resultBuilder.setValue(
                        SettingsPreferenceValue.Builder(
                            SettingsPreferenceValue.TYPE_BOOLEAN
                        ).setBooleanValue(prefValue.booleanValue)
                            .build()
                    )
                    return resultBuilder.build()
                }
                PreferenceValueProto.INT_VALUE_FIELD_NUMBER -> {
                    resultBuilder.setValue(
                        SettingsPreferenceValue.Builder(
                            SettingsPreferenceValue.TYPE_INT
                        ).setIntValue(prefValue.intValue)
                            .build()
                    )
                    return resultBuilder.build()
                }
            }
            return GetValueResult.Builder(
                GetValueResult.RESULT_UNSUPPORTED
            ).build()
        }
        else -> return null
    }
}

/** Translate Framework SET VALUE request to Catalyst SET VALUE request */
fun transformFrameworkSetValueRequest(request: SetValueRequest): PreferenceSetterRequest? {
    val valueProto = when (request.preferenceValue.type) {
        SettingsPreferenceValue.TYPE_BOOLEAN -> preferenceValueProto {
            booleanValue = request.preferenceValue.booleanValue
        }
        SettingsPreferenceValue.TYPE_INT -> preferenceValueProto {
            intValue = request.preferenceValue.intValue
        }
        else -> null
    }
    return valueProto?.let {
        PreferenceSetterRequest(request.screenKey, request.preferenceKey, it)
    }
}

/** Translate Catalyst SET VALUE result to Framework SET VALUE result */
fun transformCatalystSetValueResponse(@PreferenceSetterResult response: Int): SetValueResult {
   val resultCode = when (response) {
        PreferenceSetterResult.OK -> SetValueResult.RESULT_OK
        PreferenceSetterResult.UNAVAILABLE -> SetValueResult.RESULT_UNAVAILABLE
        PreferenceSetterResult.DISABLED -> SetValueResult.RESULT_DISABLED
        PreferenceSetterResult.UNSUPPORTED -> SetValueResult.RESULT_UNSUPPORTED
        PreferenceSetterResult.DISALLOW -> SetValueResult.RESULT_DISALLOW
        PreferenceSetterResult.REQUIRE_APP_PERMISSION ->
            SetValueResult.RESULT_REQUIRE_APP_PERMISSION
        PreferenceSetterResult.REQUIRE_USER_AGREEMENT -> SetValueResult.RESULT_REQUIRE_USER_CONSENT
        PreferenceSetterResult.RESTRICTED -> SetValueResult.RESULT_RESTRICTED
       PreferenceSetterResult.INVALID_REQUEST -> SetValueResult.RESULT_INVALID_REQUEST
        else -> SetValueResult.RESULT_INTERNAL_ERROR
    }
    return SetValueResult.Builder(resultCode).build()
}

private fun PreferenceProto.toMetadata(
    context: Context,
    screenKey: String
): SettingsPreferenceMetadata {
    val sensitivity = when (sensitivityLevel) {
        SensitivityLevel.NO_SENSITIVITY -> SettingsPreferenceMetadata.NO_SENSITIVITY
        SensitivityLevel.LOW_SENSITIVITY -> SettingsPreferenceMetadata.EXPECT_POST_CONFIRMATION
        SensitivityLevel.MEDIUM_SENSITIVITY -> SettingsPreferenceMetadata.EXPECT_PRE_CONFIRMATION
        else -> SettingsPreferenceMetadata.NO_DIRECT_ACCESS
    }
    return SettingsPreferenceMetadata.Builder(screenKey, key)
        .setTitle(title.getText(context))
        .setSummary(summary.getText(context))
        .setEnabled(enabled)
        .setAvailable(available)
        .setRestricted(restricted)
        .setWritable(persistent)
        .setLaunchIntent(launchIntent.toIntent())
        .setWriteSensitivity(sensitivity)
        .build()
}
+0 −235
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.service

import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.platform.test.annotations.RequiresFlagsEnabled
import android.platform.test.flag.junit.CheckFlagsRule
import android.platform.test.flag.junit.DeviceFlagsValueProvider
import android.service.settings.preferences.GetValueRequest
import android.service.settings.preferences.GetValueResult
import android.service.settings.preferences.SetValueRequest
import android.service.settings.preferences.SetValueResult
import android.service.settings.preferences.SettingsPreferenceMetadata
import android.service.settings.preferences.SettingsPreferenceValue
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.homepage.SettingsHomepageActivity
import com.android.settingslib.flags.Flags.FLAG_SETTINGS_CATALYST
import com.android.settingslib.graph.PreferenceCoordinate
import com.android.settingslib.graph.PreferenceGetterErrorCode
import com.android.settingslib.graph.PreferenceGetterFlags
import com.android.settingslib.graph.PreferenceGetterResponse
import com.android.settingslib.graph.PreferenceSetterResult
import com.android.settingslib.graph.proto.PreferenceProto
import com.android.settingslib.graph.proto.PreferenceValueProto
import com.android.settingslib.graph.proto.TextProto
import com.android.settingslib.graph.toProto
import com.android.settingslib.metadata.SensitivityLevel
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
@RequiresFlagsEnabled(FLAG_SETTINGS_CATALYST)
class PreferenceServiceRequestTransformerTest {

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

    @Test
    fun transformFrameworkGetValueRequest_returnsValidCatalystRequest() {
        val fRequest = GetValueRequest.Builder("screen", "pref").build()
        val cRequest = transformFrameworkGetValueRequest(fRequest)
        with(cRequest) {
            assertThat(preferences).hasLength(1)
            assertThat(preferences.first().screenKey).isEqualTo(fRequest.screenKey)
            assertThat(preferences.first().key).isEqualTo(fRequest.preferenceKey)
            assertThat(flags).isEqualTo(PreferenceGetterFlags.ALL)
        }
    }

    @Test
    fun transformCatalystGetValueResponse_success_returnsValidFrameworkResponse() {
        val context: Context = ApplicationProvider.getApplicationContext()
        val fRequest = GetValueRequest.Builder("screen", "key").build()
        val cResult = PreferenceGetterResponse(
            emptyMap(),
            mapOf(
                PreferenceCoordinate(fRequest.screenKey, fRequest.preferenceKey) to
                        PreferenceProto.newBuilder()
                            .setKey("key")
                            .setTitle(TextProto.newBuilder().setString("title"))
                            .setSummary(TextProto.newBuilder().setString("summary"))
                            .setEnabled(true)
                            .setAvailable(true)
                            .setRestricted(true)
                            .setPersistent(true)
                            .setSensitivityLevel(SensitivityLevel.LOW_SENSITIVITY)
                            .setLaunchIntent(
                                Intent(context, SettingsHomepageActivity::class.java).toProto()
                            )
                            .setValue(PreferenceValueProto.newBuilder().setBooleanValue(true))
                            .build()
            )
        )
        val fResult = transformCatalystGetValueResponse(context, fRequest, cResult)
        assertThat(fResult!!.resultCode).isEqualTo(GetValueResult.RESULT_OK)
        with(fResult.metadata!!) {
            assertThat(title).isEqualTo("title")
            assertThat(summary).isEqualTo("summary")
            assertThat(isEnabled).isTrue()
            assertThat(isAvailable).isTrue()
            assertThat(isRestricted).isTrue()
            assertThat(isWritable).isTrue()
            assertThat(writeSensitivity)
                .isEqualTo(SettingsPreferenceMetadata.EXPECT_POST_CONFIRMATION)
            assertThat(launchIntent).isNotNull()
            assertThat(launchIntent!!.component!!.className)
                .isEqualTo(SettingsHomepageActivity::class.java.name)
        }
        with(fResult.value!!) {
            assertThat(type).isEqualTo(SettingsPreferenceValue.TYPE_BOOLEAN)
            assertThat(booleanValue).isTrue()
        }
    }

    @Test
    fun transformCatalystGetValueResponse_failure_returnsValidFrameworkResponse() {
        val context: Context = ApplicationProvider.getApplicationContext()
        val fRequest = GetValueRequest.Builder("screen", "key").build()
        val cResult = PreferenceGetterResponse(
            mapOf(
                PreferenceCoordinate(fRequest.screenKey, fRequest.preferenceKey) to
                        PreferenceGetterErrorCode.NOT_FOUND
            ),
            emptyMap()
        )
        val fResult = transformCatalystGetValueResponse(context, fRequest, cResult)
        with(fResult!!) {
            assertThat(resultCode).isEqualTo(GetValueResult.RESULT_UNSUPPORTED)
            assertThat(metadata).isNull()
            assertThat(value).isNull()
        }
    }

    @Test
    fun transformCatalystGetValueResponse_invalidResponse_returnsNull() {
        val context: Context = ApplicationProvider.getApplicationContext()
        val fRequest = GetValueRequest.Builder("screen", "key").build()
        val cResult = PreferenceGetterResponse(emptyMap(), emptyMap())
        val fResult = transformCatalystGetValueResponse(context, fRequest, cResult)
        assertThat(fResult).isNull()
    }

    @Test
    fun transformFrameworkSetValueRequest_typeBoolean_returnsValidCatalystRequest() {
        val fRequest = SetValueRequest.Builder(
            "screen",
            "pref",
            SettingsPreferenceValue.Builder(SettingsPreferenceValue.TYPE_BOOLEAN)
                .setBooleanValue(true)
                .build()
        ).build()
        val cRequest = transformFrameworkSetValueRequest(fRequest)
        with(cRequest!!) {
            assertThat(screenKey).isEqualTo(fRequest.screenKey)
            assertThat(key).isEqualTo(fRequest.preferenceKey)
            assertThat(value.hasBooleanValue()).isTrue()
            assertThat(value.booleanValue).isTrue()
        }
    }

    @Test
    fun transformFrameworkSetValueRequest_typeInt_returnsValidCatalystRequest() {
        val fRequest = SetValueRequest.Builder(
            "screen",
            "pref",
            SettingsPreferenceValue.Builder(SettingsPreferenceValue.TYPE_INT)
                .setIntValue(5)
                .build()
        ).build()
        val cRequest = transformFrameworkSetValueRequest(fRequest)
        with(cRequest!!) {
            assertThat(screenKey).isEqualTo(fRequest.screenKey)
            assertThat(key).isEqualTo(fRequest.preferenceKey)
            assertThat(value.hasIntValue()).isTrue()
            assertThat(value.intValue).isEqualTo(5)
        }
    }

    @Test
    fun transformFrameworkSetValueRequest_typeString_returnsNull() {
        val fRequest = SetValueRequest.Builder(
            "screen",
            "pref",
            SettingsPreferenceValue.Builder(SettingsPreferenceValue.TYPE_STRING)
                .setStringValue("value")
                .build()
        ).build()
        val cRequest = transformFrameworkSetValueRequest(fRequest)
        assertThat(cRequest).isNull()
    }

    @Test
    fun transformCatalystSetValueResponse_returnsValidFrameworkResponse() {
        assertThat(
            transformCatalystSetValueResponse(PreferenceSetterResult.OK).resultCode
        ).isEqualTo(SetValueResult.RESULT_OK)

        assertThat(
            transformCatalystSetValueResponse(PreferenceSetterResult.UNAVAILABLE).resultCode
        ).isEqualTo(SetValueResult.RESULT_UNAVAILABLE)

        assertThat(
            transformCatalystSetValueResponse(PreferenceSetterResult.DISABLED).resultCode
        ).isEqualTo(SetValueResult.RESULT_DISABLED)

        assertThat(
            transformCatalystSetValueResponse(PreferenceSetterResult.UNSUPPORTED).resultCode
        ).isEqualTo(SetValueResult.RESULT_UNSUPPORTED)

        assertThat(
            transformCatalystSetValueResponse(PreferenceSetterResult.DISALLOW).resultCode
        ).isEqualTo(SetValueResult.RESULT_DISALLOW)

        assertThat(
            transformCatalystSetValueResponse(PreferenceSetterResult.REQUIRE_APP_PERMISSION)
                .resultCode
        ).isEqualTo(SetValueResult.RESULT_REQUIRE_APP_PERMISSION)

        assertThat(
            transformCatalystSetValueResponse(PreferenceSetterResult.REQUIRE_USER_AGREEMENT)
                .resultCode
        ).isEqualTo(SetValueResult.RESULT_REQUIRE_USER_CONSENT)

        assertThat(
            transformCatalystSetValueResponse(PreferenceSetterResult.RESTRICTED).resultCode
        ).isEqualTo(SetValueResult.RESULT_RESTRICTED)

        assertThat(
            transformCatalystSetValueResponse(PreferenceSetterResult.INVALID_REQUEST).resultCode
        ).isEqualTo(SetValueResult.RESULT_INVALID_REQUEST)

        assertThat(
            transformCatalystSetValueResponse(PreferenceSetterResult.INTERNAL_ERROR).resultCode
        ).isEqualTo(SetValueResult.RESULT_INTERNAL_ERROR)
    }
}
 No newline at end of file