Loading packages/SystemUI/Android.bp +1 −2 Original line number Diff line number Diff line Loading @@ -179,8 +179,7 @@ android_library { "src/**/*.kt", "src/**/*.java", "src/**/I*.aidl", "src-release/**/*.kt", "src-release/**/*.java", ":ReleaseJavaFiles", ], static_libs: [ "SystemUIAnimationLib", Loading packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt +44 −2 Original line number Diff line number Diff line Loading @@ -18,16 +18,22 @@ package com.android.systemui.flags import android.content.Context import android.content.Intent import android.provider.Settings import androidx.concurrent.futures.CallbackToFutureAdapter import com.google.common.util.concurrent.ListenableFuture import org.json.JSONException import org.json.JSONObject class FlagManager constructor(val context: Context) { class FlagManager constructor(val context: Context) : FlagReader { companion object { const val RECEIVING_PACKAGE = "com.android.systemui" const val ACTION_SET_FLAG = "com.android.systemui.action.SET_FLAG" const val FLAGS_PERMISSION = "com.android.systemui.permission.FLAGS" const val FIELD_ID = "id" const val FIELD_VALUE = "value" const val FIELD_TYPE = "type" const val TYPE_BOOLEAN = "boolean" private const val SETTINGS_PREFIX = "systemui/flags" } fun getFlagsFuture(): ListenableFuture<Collection<Flag<*>>> { Loading @@ -54,6 +60,28 @@ class FlagManager constructor(val context: Context) { context.sendBroadcast(intent) } override fun isEnabled(id: Int, def: Boolean): Boolean { return isEnabled(id) ?: def } /** Returns the stored value or null if not set. */ fun isEnabled(id: Int): Boolean? { val data: String = Settings.Secure.getString( context.contentResolver, keyToSettingsPrefix(id)) if (data.isEmpty()) { return null } val json: JSONObject try { json = JSONObject(data) return if (!assertType(json, TYPE_BOOLEAN)) { null } else json.getBoolean(FIELD_VALUE) } catch (e: JSONException) { throw InvalidFlagStorageException() } } private fun createIntent(id: Int): Intent { val intent = Intent(ACTION_SET_FLAG) intent.setPackage(RECEIVING_PACKAGE) Loading @@ -61,4 +89,18 @@ class FlagManager constructor(val context: Context) { return intent } fun keyToSettingsPrefix(key: Int): String? { return SETTINGS_PREFIX + "/" + key } private fun assertType(json: JSONObject, type: String): Boolean { return try { json.getString(FIELD_TYPE) == TYPE_BOOLEAN } catch (e: JSONException) { false } } } class InvalidFlagStorageException : Exception("Data found but is invalid") No newline at end of file packages/SystemUI/src/com/android/systemui/flags/FlagReader.java→packages/SystemUI/shared/src/com/android/systemui/flags/FlagReader.kt +13 −15 Original line number Diff line number Diff line Loading @@ -13,28 +13,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.flags; package com.android.systemui.flags /** * Plugin for loading flag values */ public interface FlagReader { interface FlagReader { /** Returns a boolean value for the given flag. */ default boolean isEnabled(int id, boolean def) { return def; fun isEnabled(id: Int, def: Boolean): Boolean { return def } /** Add a listener to be alerted when any flag changes. */ default void addListener(Listener listener) {} fun addListener(listener: Listener) {} /** Remove a listener to be alerted when any flag changes. */ default void removeListener(Listener listener) {} fun removeListener(listener: Listener) {} /** A simple listener to be alerted when a flag changes. */ interface Listener { /** */ void onFlagChanged(int id); fun onFlagChanged(id: Int) } } No newline at end of file packages/SystemUI/src-debug/com/android/systemui/flags/FeatureFlagManager.java +13 −36 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ import androidx.annotation.NonNull; import com.android.systemui.Dumpable; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dump.DumpManager; import com.android.systemui.util.settings.SecureSettings; import org.json.JSONException; import org.json.JSONObject; Loading @@ -58,18 +59,16 @@ import javax.inject.Inject; public class FeatureFlagManager implements FlagReader, FlagWriter, Dumpable { private static final String TAG = "SysUIFlags"; private static final String SYSPROP_PREFIX = "persist.systemui.flag_"; private static final String FIELD_TYPE = "type"; private static final String TYPE_BOOLEAN = "boolean"; private final SystemPropertiesHelper mSystemPropertiesHelper; private final FlagManager mFlagManager; private final SecureSettings mSecureSettings; private final Map<Integer, Boolean> mBooleanFlagCache = new HashMap<>(); @Inject public FeatureFlagManager(SystemPropertiesHelper systemPropertiesHelper, Context context, public FeatureFlagManager(FlagManager flagManager, SecureSettings secureSettings, Context context, DumpManager dumpManager) { mSystemPropertiesHelper = systemPropertiesHelper; mFlagManager = flagManager; mSecureSettings = secureSettings; IntentFilter filter = new IntentFilter(ACTION_SET_FLAG); context.registerReceiver(mReceiver, filter, FLAGS_PERMISSION, null); dumpManager.registerDumpable(TAG, this); Loading @@ -88,20 +87,10 @@ public class FeatureFlagManager implements FlagReader, FlagWriter, Dumpable { /** Returns the stored value or null if not set. */ private Boolean isEnabledInternal(int id) { String data = mSystemPropertiesHelper.get(keyToSysPropKey(id)); if (data.isEmpty()) { return null; } JSONObject json; try { json = new JSONObject(data); if (!assertType(json, TYPE_BOOLEAN)) { return null; } return json.getBoolean(FIELD_VALUE); } catch (JSONException e) { eraseInternal(id); // Don't restart SystemUI in this case. return mFlagManager.isEnabled(id); } catch (Exception e) { eraseInternal(id); } return null; } Loading @@ -116,9 +105,9 @@ public class FeatureFlagManager implements FlagReader, FlagWriter, Dumpable { JSONObject json = new JSONObject(); try { json.put(FIELD_TYPE, TYPE_BOOLEAN); json.put(FlagManager.FIELD_TYPE, FlagManager.TYPE_BOOLEAN); json.put(FIELD_VALUE, value); mSystemPropertiesHelper.set(keyToSysPropKey(id), json.toString()); mSecureSettings.putString(mFlagManager.keyToSettingsPrefix(id), json.toString()); Log.i(TAG, "Set id " + id + " to " + value); restartSystemUI(); } catch (JSONException e) { Loading @@ -135,7 +124,7 @@ public class FeatureFlagManager implements FlagReader, FlagWriter, Dumpable { /** Works just like {@link #eraseFlag(int)} except that it doesn't restart SystemUI. */ private void eraseInternal(int id) { // We can't actually "erase" things from sysprops, but we can set them to empty! mSystemPropertiesHelper.set(keyToSysPropKey(id), ""); mSecureSettings.putString(mFlagManager.keyToSettingsPrefix(id), ""); Log.i(TAG, "Erase id " + id); } Loading @@ -151,18 +140,6 @@ public class FeatureFlagManager implements FlagReader, FlagWriter, Dumpable { System.exit(0); } private static String keyToSysPropKey(int key) { return SYSPROP_PREFIX + key; } private static boolean assertType(JSONObject json, String type) { try { return json.getString(FIELD_TYPE).equals(TYPE_BOOLEAN); } catch (JSONException e) { return false; } } private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Loading packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt 0 → 100644 +30 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 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.systemui.flags import android.content.Context import dagger.Module import dagger.Provides @Module object FlagsModule { @JvmStatic @Provides fun provideFlagManager(context: Context): FlagManager { return FlagManager(context) } } No newline at end of file Loading
packages/SystemUI/Android.bp +1 −2 Original line number Diff line number Diff line Loading @@ -179,8 +179,7 @@ android_library { "src/**/*.kt", "src/**/*.java", "src/**/I*.aidl", "src-release/**/*.kt", "src-release/**/*.java", ":ReleaseJavaFiles", ], static_libs: [ "SystemUIAnimationLib", Loading
packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt +44 −2 Original line number Diff line number Diff line Loading @@ -18,16 +18,22 @@ package com.android.systemui.flags import android.content.Context import android.content.Intent import android.provider.Settings import androidx.concurrent.futures.CallbackToFutureAdapter import com.google.common.util.concurrent.ListenableFuture import org.json.JSONException import org.json.JSONObject class FlagManager constructor(val context: Context) { class FlagManager constructor(val context: Context) : FlagReader { companion object { const val RECEIVING_PACKAGE = "com.android.systemui" const val ACTION_SET_FLAG = "com.android.systemui.action.SET_FLAG" const val FLAGS_PERMISSION = "com.android.systemui.permission.FLAGS" const val FIELD_ID = "id" const val FIELD_VALUE = "value" const val FIELD_TYPE = "type" const val TYPE_BOOLEAN = "boolean" private const val SETTINGS_PREFIX = "systemui/flags" } fun getFlagsFuture(): ListenableFuture<Collection<Flag<*>>> { Loading @@ -54,6 +60,28 @@ class FlagManager constructor(val context: Context) { context.sendBroadcast(intent) } override fun isEnabled(id: Int, def: Boolean): Boolean { return isEnabled(id) ?: def } /** Returns the stored value or null if not set. */ fun isEnabled(id: Int): Boolean? { val data: String = Settings.Secure.getString( context.contentResolver, keyToSettingsPrefix(id)) if (data.isEmpty()) { return null } val json: JSONObject try { json = JSONObject(data) return if (!assertType(json, TYPE_BOOLEAN)) { null } else json.getBoolean(FIELD_VALUE) } catch (e: JSONException) { throw InvalidFlagStorageException() } } private fun createIntent(id: Int): Intent { val intent = Intent(ACTION_SET_FLAG) intent.setPackage(RECEIVING_PACKAGE) Loading @@ -61,4 +89,18 @@ class FlagManager constructor(val context: Context) { return intent } fun keyToSettingsPrefix(key: Int): String? { return SETTINGS_PREFIX + "/" + key } private fun assertType(json: JSONObject, type: String): Boolean { return try { json.getString(FIELD_TYPE) == TYPE_BOOLEAN } catch (e: JSONException) { false } } } class InvalidFlagStorageException : Exception("Data found but is invalid") No newline at end of file
packages/SystemUI/src/com/android/systemui/flags/FlagReader.java→packages/SystemUI/shared/src/com/android/systemui/flags/FlagReader.kt +13 −15 Original line number Diff line number Diff line Loading @@ -13,28 +13,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.flags; package com.android.systemui.flags /** * Plugin for loading flag values */ public interface FlagReader { interface FlagReader { /** Returns a boolean value for the given flag. */ default boolean isEnabled(int id, boolean def) { return def; fun isEnabled(id: Int, def: Boolean): Boolean { return def } /** Add a listener to be alerted when any flag changes. */ default void addListener(Listener listener) {} fun addListener(listener: Listener) {} /** Remove a listener to be alerted when any flag changes. */ default void removeListener(Listener listener) {} fun removeListener(listener: Listener) {} /** A simple listener to be alerted when a flag changes. */ interface Listener { /** */ void onFlagChanged(int id); fun onFlagChanged(id: Int) } } No newline at end of file
packages/SystemUI/src-debug/com/android/systemui/flags/FeatureFlagManager.java +13 −36 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ import androidx.annotation.NonNull; import com.android.systemui.Dumpable; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dump.DumpManager; import com.android.systemui.util.settings.SecureSettings; import org.json.JSONException; import org.json.JSONObject; Loading @@ -58,18 +59,16 @@ import javax.inject.Inject; public class FeatureFlagManager implements FlagReader, FlagWriter, Dumpable { private static final String TAG = "SysUIFlags"; private static final String SYSPROP_PREFIX = "persist.systemui.flag_"; private static final String FIELD_TYPE = "type"; private static final String TYPE_BOOLEAN = "boolean"; private final SystemPropertiesHelper mSystemPropertiesHelper; private final FlagManager mFlagManager; private final SecureSettings mSecureSettings; private final Map<Integer, Boolean> mBooleanFlagCache = new HashMap<>(); @Inject public FeatureFlagManager(SystemPropertiesHelper systemPropertiesHelper, Context context, public FeatureFlagManager(FlagManager flagManager, SecureSettings secureSettings, Context context, DumpManager dumpManager) { mSystemPropertiesHelper = systemPropertiesHelper; mFlagManager = flagManager; mSecureSettings = secureSettings; IntentFilter filter = new IntentFilter(ACTION_SET_FLAG); context.registerReceiver(mReceiver, filter, FLAGS_PERMISSION, null); dumpManager.registerDumpable(TAG, this); Loading @@ -88,20 +87,10 @@ public class FeatureFlagManager implements FlagReader, FlagWriter, Dumpable { /** Returns the stored value or null if not set. */ private Boolean isEnabledInternal(int id) { String data = mSystemPropertiesHelper.get(keyToSysPropKey(id)); if (data.isEmpty()) { return null; } JSONObject json; try { json = new JSONObject(data); if (!assertType(json, TYPE_BOOLEAN)) { return null; } return json.getBoolean(FIELD_VALUE); } catch (JSONException e) { eraseInternal(id); // Don't restart SystemUI in this case. return mFlagManager.isEnabled(id); } catch (Exception e) { eraseInternal(id); } return null; } Loading @@ -116,9 +105,9 @@ public class FeatureFlagManager implements FlagReader, FlagWriter, Dumpable { JSONObject json = new JSONObject(); try { json.put(FIELD_TYPE, TYPE_BOOLEAN); json.put(FlagManager.FIELD_TYPE, FlagManager.TYPE_BOOLEAN); json.put(FIELD_VALUE, value); mSystemPropertiesHelper.set(keyToSysPropKey(id), json.toString()); mSecureSettings.putString(mFlagManager.keyToSettingsPrefix(id), json.toString()); Log.i(TAG, "Set id " + id + " to " + value); restartSystemUI(); } catch (JSONException e) { Loading @@ -135,7 +124,7 @@ public class FeatureFlagManager implements FlagReader, FlagWriter, Dumpable { /** Works just like {@link #eraseFlag(int)} except that it doesn't restart SystemUI. */ private void eraseInternal(int id) { // We can't actually "erase" things from sysprops, but we can set them to empty! mSystemPropertiesHelper.set(keyToSysPropKey(id), ""); mSecureSettings.putString(mFlagManager.keyToSettingsPrefix(id), ""); Log.i(TAG, "Erase id " + id); } Loading @@ -151,18 +140,6 @@ public class FeatureFlagManager implements FlagReader, FlagWriter, Dumpable { System.exit(0); } private static String keyToSysPropKey(int key) { return SYSPROP_PREFIX + key; } private static boolean assertType(JSONObject json, String type) { try { return json.getString(FIELD_TYPE).equals(TYPE_BOOLEAN); } catch (JSONException e) { return false; } } private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Loading
packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt 0 → 100644 +30 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 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.systemui.flags import android.content.Context import dagger.Module import dagger.Provides @Module object FlagsModule { @JvmStatic @Provides fun provideFlagManager(context: Context): FlagManager { return FlagManager(context) } } No newline at end of file