• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2025 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 package com.android.settingslib.devicestate
17 
18 import android.content.Context
19 import android.database.ContentObserver
20 import android.os.Handler
21 import android.os.UserHandle
22 import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK
23 import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORED
24 import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED
25 import android.util.IndentingPrintWriter
26 import android.util.Log
27 import android.util.SparseIntArray
28 import com.android.internal.R
29 import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager.DeviceStateAutoRotateSettingListener
30 import com.android.window.flags.Flags
31 import java.io.PrintWriter
32 import java.util.concurrent.Executor
33 
34 /**
35  * Implementation of [DeviceStateAutoRotateSettingManager]. This implementation is a part of
36  * refactoring, it should be used when [Flags.FLAG_ENABLE_DEVICE_STATE_AUTO_ROTATE_SETTING_REFACTOR]
37  * is enabled.
38  */
39 class DeviceStateAutoRotateSettingManagerImpl(
40     context: Context,
41     backgroundExecutor: Executor,
42     private val secureSettings: SecureSettings,
43     private val mainHandler: Handler,
44     private val posturesHelper: PosturesHelper,
45 ) : DeviceStateAutoRotateSettingManager {
46     // TODO: b/397928958 rename the fields and apis from rotationLock to autoRotate.
47 
48     private val settingListeners: MutableList<DeviceStateAutoRotateSettingListener> =
49         mutableListOf()
50     private val fallbackPostureMap = SparseIntArray()
51     private val settableDeviceState: MutableList<SettableDeviceState> = mutableListOf()
52 
53     private val autoRotateSettingValue: String
54         get() = secureSettings.getStringForUser(DEVICE_STATE_ROTATION_LOCK, UserHandle.USER_CURRENT)
55 
56     init {
57         loadAutoRotateDeviceStates(context)
58         val contentObserver =
59             object : ContentObserver(mainHandler) {
60                 override fun onChange(selfChange: Boolean) = notifyListeners()
61             }
62         backgroundExecutor.execute {
63             secureSettings.registerContentObserver(
64                 DEVICE_STATE_ROTATION_LOCK, false, contentObserver, UserHandle.USER_CURRENT
65             )
66         }
67     }
68 
69     override fun registerListener(settingListener: DeviceStateAutoRotateSettingListener) {
70         settingListeners.add(settingListener)
71     }
72 
73     override fun unregisterListener(settingListener: DeviceStateAutoRotateSettingListener) {
74         if (!settingListeners.remove(settingListener)) {
75             Log.w(TAG, "Attempting to unregister a listener hadn't been registered")
76         }
77     }
78 
79     override fun getRotationLockSetting(deviceState: Int): Int {
80         val devicePosture = posturesHelper.deviceStateToPosture(deviceState)
81         val serializedSetting = autoRotateSettingValue
82         val autoRotateSetting = extractSettingForDevicePosture(devicePosture, serializedSetting)
83 
84         // If the setting is ignored for this posture, check the fallback posture.
85         if (autoRotateSetting == DEVICE_STATE_ROTATION_LOCK_IGNORED) {
86             val fallbackPosture =
87                 fallbackPostureMap.get(devicePosture, DEVICE_STATE_ROTATION_LOCK_IGNORED)
88             return extractSettingForDevicePosture(fallbackPosture, serializedSetting)
89         }
90 
91         return autoRotateSetting
92     }
93 
94     override fun isRotationLocked(deviceState: Int) =
95         getRotationLockSetting(deviceState) == DEVICE_STATE_ROTATION_LOCK_LOCKED
96 
97     override fun isRotationLockedForAllStates(): Boolean =
98         convertSerializedSettingToMap(autoRotateSettingValue).all { (_, value) ->
99             value == DEVICE_STATE_ROTATION_LOCK_LOCKED
100         }
101 
102     override fun getSettableDeviceStates(): List<SettableDeviceState> = settableDeviceState
103 
104     override fun updateSetting(deviceState: Int, autoRotate: Boolean) {
105         // TODO: b/350946537 - Create IPC to update the setting, and call it here.
106         throw UnsupportedOperationException("API updateSetting is not implemented yet")
107     }
108 
109     override fun dump(writer: PrintWriter, args: Array<out String>?) {
110         val indentingWriter = IndentingPrintWriter(writer)
111         indentingWriter.println("DeviceStateAutoRotateSettingManagerImpl")
112         indentingWriter.increaseIndent()
113         indentingWriter.println("fallbackPostureMap: $fallbackPostureMap")
114         indentingWriter.println("settableDeviceState: $settableDeviceState")
115         indentingWriter.decreaseIndent()
116     }
117 
118     private fun notifyListeners() =
119         settingListeners.forEach { listener -> listener.onSettingsChanged() }
120 
121     private fun loadAutoRotateDeviceStates(context: Context) {
122         val perDeviceStateAutoRotateDefaults =
123             context.resources.getStringArray(R.array.config_perDeviceStateRotationLockDefaults)
124         for (entry in perDeviceStateAutoRotateDefaults) {
125             entry.parsePostureEntry()?.let { (posture, autoRotate, fallbackPosture) ->
126                 if (autoRotate == DEVICE_STATE_ROTATION_LOCK_IGNORED && fallbackPosture != null) {
127                     fallbackPostureMap.put(posture, fallbackPosture)
128                 }
129                 settableDeviceState.add(
130                     SettableDeviceState(posture, autoRotate != DEVICE_STATE_ROTATION_LOCK_IGNORED)
131                 )
132             }
133         }
134     }
135 
136     private fun convertSerializedSettingToMap(serializedSetting: String): Map<Int, Int> {
137         if (serializedSetting.isEmpty()) return emptyMap()
138         return try {
139             serializedSetting
140                 .split(SEPARATOR_REGEX)
141                 .hasEvenSize()
142                 .chunked(2)
143                 .mapNotNull(::parsePostureSettingPair)
144                 .toMap()
145         } catch (e: Exception) {
146             Log.w(
147                 TAG,
148                 "Invalid format in serializedSetting=$serializedSetting: ${e.message}"
149             )
150             return emptyMap()
151         }
152     }
153 
154     private fun List<String>.hasEvenSize(): List<String> {
155         if (this.size % 2 != 0) {
156             throw IllegalStateException("Odd number of elements in the list")
157         }
158         return this
159     }
160 
161     private fun parsePostureSettingPair(settingPair: List<String>): Pair<Int, Int>? {
162         return settingPair.let { (keyStr, valueStr) ->
163             val key = keyStr.toIntOrNull()
164             val value = valueStr.toIntOrNull()
165             if (key != null && value != null && value in 0..2) {
166                 key to value
167             } else {
168                 Log.w(TAG, "Invalid key or value in pair: $keyStr, $valueStr")
169                 null // Invalid pair, skip it
170             }
171         }
172     }
173 
174     private fun extractSettingForDevicePosture(
175         devicePosture: Int,
176         serializedSetting: String
177     ): Int =
178         convertSerializedSettingToMap(serializedSetting)[devicePosture]
179             ?: DEVICE_STATE_ROTATION_LOCK_IGNORED
180 
181     private fun String.parsePostureEntry(): Triple<Int, Int, Int?>? {
182         val values = split(SEPARATOR_REGEX)
183         if (values.size !in 2..3) { // It should contain 2 or 3 values.
184             Log.w(TAG, "Invalid number of values in entry: '$this'")
185             return null
186         }
187         return try {
188             val posture = values[0].toInt()
189             val rotationLockSetting = values[1].toInt()
190             val fallbackPosture = if (values.size == 3) values[2].toIntOrNull() else null
191             Triple(posture, rotationLockSetting, fallbackPosture)
192         } catch (e: NumberFormatException) {
193             Log.w(TAG, "Invalid number format in '$this': ${e.message}")
194             null
195         }
196     }
197 
198     companion object {
199         private const val TAG = "DeviceStateAutoRotate"
200         private const val SEPARATOR_REGEX = ":"
201     }
202 }
203