• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 
17 package com.android.systemui.flags;
18 
19 import static com.android.systemui.flags.FlagsCommonModule.ALL_FLAGS;
20 
21 import static java.util.Objects.requireNonNull;
22 
23 import android.content.res.Resources;
24 
25 import androidx.annotation.NonNull;
26 
27 import com.android.systemui.dagger.SysUISingleton;
28 import com.android.systemui.dagger.qualifiers.Main;
29 
30 import org.jetbrains.annotations.NotNull;
31 
32 import java.io.PrintWriter;
33 import java.util.HashMap;
34 import java.util.Map;
35 
36 import javax.inject.Inject;
37 import javax.inject.Named;
38 
39 /**
40  * Default implementation of the a Flag manager that returns default values for release builds
41  *
42  * There's a version of this file in src-debug which allows overriding, and has documentation about
43  * how to set flags.
44  */
45 @SysUISingleton
46 public class FeatureFlagsClassicRelease implements FeatureFlagsClassic {
47     static final String TAG = "SysUIFlags";
48 
49     private final Resources mResources;
50     private final SystemPropertiesHelper mSystemProperties;
51     private final ServerFlagReader mServerFlagReader;
52     private final Restarter mRestarter;
53     private final Map<String, Flag<?>> mAllFlags;
54     private final Map<String, Boolean> mBooleanCache = new HashMap<>();
55     private final Map<String, String> mStringCache = new HashMap<>();
56     private final Map<String, Integer> mIntCache = new HashMap<>();
57 
58     private final ServerFlagReader.ChangeListener mOnPropertiesChanged =
59             new ServerFlagReader.ChangeListener() {
60                 @Override
61                 public void onChange(Flag<?> flag, String value) {
62                     boolean shouldRestart = false;
63                     if (mBooleanCache.containsKey(flag.getName())) {
64                         boolean newValue = value == null ? false : Boolean.parseBoolean(value);
65                         if (mBooleanCache.get(flag.getName()) != newValue) {
66                             shouldRestart = true;
67                         }
68                     } else if (mStringCache.containsKey(flag.getName())) {
69                         String newValue = value == null ? "" : value;
70                         if (!mStringCache.get(flag.getName()).equals(newValue)) {
71                             shouldRestart = true;
72                         }
73                     } else if (mIntCache.containsKey(flag.getName())) {
74                         int newValue = 0;
75                         try {
76                             newValue = value == null ? 0 : Integer.parseInt(value);
77                         } catch (NumberFormatException e) {
78                         }
79                         if (mIntCache.get(flag.getName()) != newValue) {
80                             shouldRestart = true;
81                         }
82                     }
83                     if (shouldRestart) {
84                         mRestarter.restartSystemUI(
85                                 "Server flag change: " + flag.getNamespace() + "."
86                                         + flag.getName());
87                     }
88                 }
89             };
90 
91     @Inject
FeatureFlagsClassicRelease( @ain Resources resources, SystemPropertiesHelper systemProperties, ServerFlagReader serverFlagReader, @Named(ALL_FLAGS) Map<String, Flag<?>> allFlags, Restarter restarter)92     public FeatureFlagsClassicRelease(
93             @Main Resources resources,
94             SystemPropertiesHelper systemProperties,
95             ServerFlagReader serverFlagReader,
96             @Named(ALL_FLAGS) Map<String, Flag<?>> allFlags,
97             Restarter restarter) {
98         mResources = resources;
99         mSystemProperties = systemProperties;
100         mServerFlagReader = serverFlagReader;
101         mAllFlags = allFlags;
102         mRestarter = restarter;
103     }
104 
105     /** Call after construction to setup listeners. */
init()106     void init() {
107         mServerFlagReader.listenForChanges(mAllFlags.values(), mOnPropertiesChanged);
108     }
109 
110     @Override
addListener(@onNull Flag<?> flag, @NonNull Listener listener)111     public void addListener(@NonNull Flag<?> flag, @NonNull Listener listener) {
112     }
113 
114     @Override
removeListener(@onNull Listener listener)115     public void removeListener(@NonNull Listener listener) {
116     }
117 
118     @Override
isEnabled(@otNull UnreleasedFlag flag)119     public boolean isEnabled(@NotNull UnreleasedFlag flag) {
120         return false;
121     }
122 
123     @Override
isEnabled(@otNull ReleasedFlag flag)124     public boolean isEnabled(@NotNull ReleasedFlag flag) {
125         // Fill the cache.
126         return isEnabledInternal(flag.getName(),
127                 mServerFlagReader.readServerOverride(flag.getNamespace(), flag.getName(), true));
128     }
129 
130     @Override
isEnabled(ResourceBooleanFlag flag)131     public boolean isEnabled(ResourceBooleanFlag flag) {
132         // Fill the cache.
133         return isEnabledInternal(flag.getName(), mResources.getBoolean(flag.getResourceId()));
134     }
135 
136     @Override
isEnabled(SysPropBooleanFlag flag)137     public boolean isEnabled(SysPropBooleanFlag flag) {
138         // Fill the cache.
139         return isEnabledInternal(
140                 flag.getName(),
141                 mSystemProperties.getBoolean(flag.getName(), flag.getDefault()));
142     }
143 
144     /**
145      * Checks and fills the boolean cache. This is important, Always call through to this method!
146      *
147      * We use the cache as a way to decide if we need to restart the process when server-side
148      * changes occur.
149      */
isEnabledInternal(String name, boolean defaultValue)150     private boolean isEnabledInternal(String name, boolean defaultValue) {
151         // Fill the cache.
152         if (!mBooleanCache.containsKey(name)) {
153             mBooleanCache.put(name, defaultValue);
154         }
155 
156         return mBooleanCache.get(name);
157     }
158 
159     @NonNull
160     @Override
getString(@onNull StringFlag flag)161     public String getString(@NonNull StringFlag flag) {
162         // Fill the cache.
163         return getStringInternal(flag.getName(), flag.getDefault());
164     }
165 
166     @NonNull
167     @Override
getString(@onNull ResourceStringFlag flag)168     public String getString(@NonNull ResourceStringFlag flag) {
169         // Fill the cache.
170         return getStringInternal(flag.getName(),
171                 requireNonNull(mResources.getString(flag.getResourceId())));
172     }
173 
174     /**
175      * Checks and fills the String cache. This is important, Always call through to this method!
176      *
177      * We use the cache as a way to decide if we need to restart the process when server-side
178      * changes occur.
179      */
getStringInternal(String name, String defaultValue)180     private String getStringInternal(String name, String defaultValue) {
181         if (!mStringCache.containsKey(name)) {
182             mStringCache.put(name, defaultValue);
183         }
184 
185         return mStringCache.get(name);
186     }
187 
188     @Override
getInt(@onNull IntFlag flag)189     public int getInt(@NonNull IntFlag flag) {
190         // Fill the cache.
191         return getIntInternal(flag.getName(), flag.getDefault());
192     }
193 
194     @Override
getInt(@onNull ResourceIntFlag flag)195     public int getInt(@NonNull ResourceIntFlag flag) {
196         // Fill the cache.
197         return mResources.getInteger(flag.getResourceId());
198     }
199 
200     /**
201      * Checks and fills the integer cache. This is important, Always call through to this method!
202      *
203      * We use the cache as a way to decide if we need to restart the process when server-side
204      * changes occur.
205      */
getIntInternal(String name, int defaultValue)206     private int getIntInternal(String name, int defaultValue) {
207         if (!mIntCache.containsKey(name)) {
208             mIntCache.put(name, defaultValue);
209         }
210 
211         return mIntCache.get(name);
212     }
213 
214     @Override
dump(@onNull PrintWriter pw, @NonNull String[] args)215     public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
216         pw.println("can override: false");
217         Map<String, Flag<?>> knownFlags = FlagsFactory.INSTANCE.getKnownFlags();
218         pw.println("Booleans: ");
219         for (Map.Entry<String, Flag<?>> nameToFlag : knownFlags.entrySet()) {
220             Flag<?> flag = nameToFlag.getValue();
221             if (!(flag instanceof BooleanFlag)
222                     || !(flag instanceof ResourceBooleanFlag)
223                     || !(flag instanceof SysPropBooleanFlag)) {
224                 continue;
225             }
226 
227             boolean def = false;
228             if (!mBooleanCache.containsKey(flag.getName())) {
229                 if (flag instanceof SysPropBooleanFlag) {
230                     SysPropBooleanFlag f = (SysPropBooleanFlag) flag;
231                     def = mSystemProperties.getBoolean(f.getName(), f.getDefault());
232                 } else if (flag instanceof ResourceBooleanFlag) {
233                     ResourceBooleanFlag f = (ResourceBooleanFlag) flag;
234                     def = mResources.getBoolean(f.getResourceId());
235                 } else if (flag instanceof BooleanFlag) {
236                     BooleanFlag f = (BooleanFlag) flag;
237                     def = f.getDefault();
238                 }
239             }
240             pw.println(
241                     "  " + flag.getName() + ": "
242                             + (mBooleanCache.getOrDefault(flag.getName(), def)));
243         }
244 
245         pw.println("Strings: ");
246         for (Map.Entry<String, Flag<?>> nameToFlag : knownFlags.entrySet()) {
247             Flag<?> flag = nameToFlag.getValue();
248             if (!(flag instanceof StringFlag)
249                     || !(flag instanceof ResourceStringFlag)) {
250                 continue;
251             }
252 
253             String def = "";
254             if (!mBooleanCache.containsKey(flag.getName())) {
255                 if (flag instanceof ResourceStringFlag) {
256                     ResourceStringFlag f = (ResourceStringFlag) flag;
257                     def = mResources.getString(f.getResourceId());
258                 } else if (flag instanceof StringFlag) {
259                     StringFlag f = (StringFlag) flag;
260                     def = f.getDefault();
261                 }
262             }
263             String value = mStringCache.getOrDefault(flag.getName(), def);
264             pw.println(
265                     "  " + flag.getName() + ": [length=" + value.length() + "] \"" + value + "\"");
266         }
267     }
268 }
269