package {package_name};
{{ -if not is_test_mode }}
{{ if not library_exported- }}
// TODO(b/303773055): Remove the annotation after access issue is resolved.
import android.compat.annotation.UnsupportedAppUsage;
{{ -endif }}

{{ -if runtime_lookup_required }}
import android.provider.DeviceConfig;
import android.provider.DeviceConfig.Properties;


{{ -if not library_exported }}
{{ -if allow_instrumentation }}
import android.aconfig.storage.StorageInternalReader;
import android.util.Log;
{{ -endif }}
{{ -endif }}

{{ -endif }}
/** @hide */
public final class FeatureFlagsImpl implements FeatureFlags \{
{{ -if runtime_lookup_required }}
{{ -for namespace_with_flags in namespace_flags }}
    private static volatile boolean {namespace_with_flags.namespace}_is_cached = false;
{{ -endfor- }}

{{ for flag in flag_elements }}
{{- if flag.is_read_write }}
    private static boolean {flag.method_name} = {flag.default_value};
{{ -endif }}
{{ -endfor }}
{{ -if not library_exported }}
{{ -if allow_instrumentation }}
    StorageInternalReader reader;
    boolean readFromNewStorage;

    boolean useNewStorageValueAndDiscardOld = false;

    private final static String TAG = "AconfigJavaCodegen";
    private final static String SUCCESS_LOG = "success: %s value matches";
    private final static String MISMATCH_LOG = "error: %s value mismatch, new storage value is %s, old storage value is %s";
    private final static String ERROR_LOG = "error: failed to read flag value";

    private void init() \{
        if (reader != null) return;
        if (DeviceConfig.getBoolean("core_experiments_team_internal", "com.android.providers.settings.storage_test_mission_1", false)) \{
            readFromNewStorage = true;
            try \{
                reader = new StorageInternalReader("{container}", "{package_name}");
            } catch (Exception e) \{
                reader = null;
            }
        }

        useNewStorageValueAndDiscardOld =
            DeviceConfig.getBoolean("core_experiments_team_internal", "com.android.providers.settings.use_new_storage_value", false);
    }

{{ -endif }}
{{ -endif }}
{{ for namespace_with_flags in namespace_flags }}
    private void load_overrides_{namespace_with_flags.namespace}() \{
        try \{
            Properties properties = DeviceConfig.getProperties("{namespace_with_flags.namespace}");
{{ -for flag in namespace_with_flags.flags }}
{{ -if flag.is_read_write }}
            {flag.method_name} =
                properties.getBoolean(Flags.FLAG_{flag.flag_name_constant_suffix}, {flag.default_value});
{{ -endif }}
{{ -endfor }}
        } catch (NullPointerException e) \{
            throw new RuntimeException(
                "Cannot read value from namespace {namespace_with_flags.namespace} "
                + "from DeviceConfig. It could be that the code using flag "
                + "executed before SettingsProvider initialization. Please use "
                + "fixed read-only flag by adding is_fixed_read_only: true in "
                + "flag declaration.",
                e
            );
        }
        {namespace_with_flags.namespace}_is_cached = true;
{{ -if not library_exported }}
{{ -if allow_instrumentation }}
        init();
        if (readFromNewStorage && reader != null) \{
            boolean val;
            try \{
{{ -for flag in namespace_with_flags.flags }}
{{ -if flag.is_read_write }}

                val = reader.getBooleanFlagValue({flag.flag_offset});
                if (val != {flag.method_name}) \{
                    Log.w(TAG, String.format(MISMATCH_LOG, "{flag.method_name}", val, {flag.method_name}));
                }

                if (useNewStorageValueAndDiscardOld) \{
                    {flag.method_name} = val;
                }

{{ -endif }}
{{ -endfor }}
            } catch (Exception e) \{
                    Log.e(TAG, ERROR_LOG, e);
            }
        }
{{ -endif }}
{{ -endif }}
    }
{{ endfor- }}
{{ -endif }}{#- end of runtime_lookup_required #}
{{ -for flag in flag_elements }}
    @Override
{{ -if not library_exported }}
    @com.android.aconfig.annotations.AconfigFlagAccessor
    @UnsupportedAppUsage
{{ -endif }}
    public boolean {flag.method_name}() \{
{{ -if flag.is_read_write }}
        if (!{flag.device_config_namespace}_is_cached) \{
            load_overrides_{flag.device_config_namespace}();
        }
        return {flag.method_name};
{{ -else }}
        return {flag.default_value};
{{ -endif }}
    }
{{ endfor }}
}
{{ else }}
{#- Generate only stub if in test mode #}
/** @hide */
public final class FeatureFlagsImpl implements FeatureFlags \{
{{ for flag in flag_elements }}
    @Override
{{ -if not library_exported }}
    @com.android.aconfig.annotations.AconfigFlagAccessor
{{ -endif }}
    public boolean {flag.method_name}() \{
        throw new UnsupportedOperationException(
            "Method is not implemented.");
    }
{{ endfor- }}
}
{{ endif }}
