1 /*
2  * Copyright 2023 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.google.android.iwlan;
18 
19 import android.content.Context;
20 import android.os.PersistableBundle;
21 import android.support.annotation.NonNull;
22 import android.telephony.CarrierConfigManager;
23 
24 import androidx.annotation.VisibleForTesting;
25 
26 /** Class for handling IWLAN carrier configuration. */
27 public class IwlanCarrierConfig {
28     static final String PREFIX = "iwlan.";
29 
30     /**
31      * Key for setting the delay in seconds to release the IWLAN connection after a handover to
32      * WWAN. Refer to {@link #DEFAULT_HANDOVER_TO_WWAN_RELEASE_DELAY_SECOND_INT} for the default
33      * value.
34      */
35     public static final String KEY_HANDOVER_TO_WWAN_RELEASE_DELAY_SECOND_INT =
36             PREFIX + "handover_to_wwan_release_delay_second_int";
37 
38     /**
39      * Key to exclude IKE N1_MODE_CAPABILITY Notify payload during emergency session setup without
40      * affecting normal sessions. See {@link #DEFAULT_N1_MODE_EXCLUSION_FOR_EMERGENCY_SESSION_BOOL}
41      * for the default value.
42      */
43     public static final String KEY_N1_MODE_EXCLUSION_FOR_EMERGENCY_SESSION_BOOL =
44             PREFIX + "n1_mode_exclusion_for_emergency_session_bool";
45 
46     /**
47      * Key to decide whether N1 mode shall be enabled or disabled depending on 5G enabling status
48      * via the UI/UX. See {@link #DEFAULT_UPDATE_N1_MODE_ON_UI_CHANGE_BOOL} for the default value.
49      */
50     public static final String KEY_UPDATE_N1_MODE_ON_UI_CHANGE_BOOL =
51             PREFIX + "update_n1_mode_on_ui_change_bool";
52 
53     /**
54      * Boolean indicating if distinct ePDG selection for emergency sessions is enabled. Refer to
55      * {@link #DEFAULT_DISTINCT_EPDG_FOR_EMERGENCY_ALLOWED_BOOL} for the default value.
56      */
57     public static final String KEY_DISTINCT_EPDG_FOR_EMERGENCY_ALLOWED_BOOL =
58             PREFIX + "distinct_epdg_for_emergency_allowed_bool";
59 
60     /**
61      * Key to control whether the UE includes the IKE DEVICE_IDENTITY Notify payload when receiving
62      * a request. See {@link #DEFAULT_IKE_DEVICE_IDENTITY_SUPPORTED_BOOL} for the default value.
63      */
64     public static final String KEY_IKE_DEVICE_IDENTITY_SUPPORTED_BOOL =
65             PREFIX + "ike_device_identity_supported_bool";
66 
67     /**
68      * Boolean indicating if reordering ike SA transforms enabled. Refer to
69      * {@link #DEFAULT_IKE_SA_TRANSFORMS_REORDER_BOOL} for the default value.
70      */
71     public static final String KEY_IKE_SA_TRANSFORMS_REORDER_BOOL =
72             PREFIX + "ike_sa_transforms_reorder_bool";
73 
74     /**
75      * IWLAN error policy configs that determine the behavior when error happens during ePDG tunnel
76      * setup. Refer to {@link #DEFAULT_ERROR_POLICY_CONFIG_STRING} for the default value.
77      *
78      * <p>The Error Config is defined as an Array of APNs identified by "ApnName". Other than Apn
79      * names this can also have "*" value which represents that this can be used as a generic
80      * fallback when no other policy matches.
81      *
82      * <p>Each APN associated with "ApnName" has an array of "ErrorTypes". Where each element in
83      * "ErrorTypes" array defines the config for the Error. The element in "ErrorTypes" array has
84      * the following items:
85      *
86      * <ul>
87      *   <li>"ErrorType": The type of error in String. Possible error types are:
88      *       <ol>
89      *         <li>"IKE_PROTOCOL_ERROR_TYPE" refers to the Notify Error coming in Notify payload.
90      *             See https://tools.ietf.org/html/rfc4306#section-3.10.1 for global errors and
91      *             carrier specific requirements for other carrier specific error codes.
92      *         <li>"GENERIC_ERROR_TYPE" refers to the following IWLAN errors - "IO_EXCEPTION",
93      *             "TIMEOUT_EXCEPTION", "SERVER_SELECTION_FAILED" and "TUNNEL_TRANSFORM_FAILED".
94      *         <li>"*" represents that this policy is a generic fallback when no other policy
95      *             matches.
96      *       </ol>
97      *   <li>"ErrorDetails": Array of errors specifics for which the policy needs to be applied to.
98      *       Note: Array can be a mix of numbers, ranges and string formats. Following are the
99      *       currently supported formats of elements in the array:
100      *       <ol>
101      *         <li>Number or Code: "24" - Number specific to the error.
102      *         <li>Range: "9000-9050" - Range of specific errors.
103      *         <li>Any: "*" value represents that this can be applied to all ErrorDetails when there
104      *             is no specific match. This will be a single element array.
105      *         <li>String: String describing the specific error. Current allowed string values -
106      *             "IO_EXCEPTION", "TIMEOUT_EXCEPTION", "SERVER_SELECTION_FAILED" and
107      *             "TUNNEL_TRANSFORM_FAILED"
108      *       </ol>
109      *       <p>"IKE_PROTOCOL_EXCEPTION" ErrorType expects the "error_detail" to be defined only in
110      *       numbers or range of numbers. Examples: ["24"] or ["9000-9050"] or ["7", "14000-14050"]
111      *       <p>"GENERIC_ERROR_TYPE" or "*" ErrorType expects only the following to be in
112      *       "ErrorDetails" - "IO_EXCEPTION", "TIMEOUT_EXCEPTION", "SERVER_SELECTION_FAILED",
113      *       "TUNNEL_TRANSFORM_FAILED" and "*". Examples: ["IO_EXCEPTION", "TIMEOUT_EXCEPTION"] or
114      *       ["*"]
115      *   <li>"RetryArray": Array of retry times (in secs) represented in string format. Following
116      *       formats are currently supported:
117      *       <ol>
118      *         <li>["0","0", "0"] Retry immediately for maximum 3 times and then fail.
119      *         <li>[] Empty array means to fail whenever the error happens.
120      *         <li>["2", "4", "8"] Retry times are 2 secs, 4secs and 8 secs - fail after that.
121      *         <li>["5", "10", "15", "-1"] Here the "-1" represents infinite retires with the retry
122      *             time "15" the last retry number).
123      *         <li>["2+r15"] 2 seconds + random time below 15 seconds, fail after that.
124      *       </ol>
125      *       <p>When fails, by default throttle for 24 hours.
126      *   <li>"UnthrottlingEvents": Events for which the retry time can be unthrottled in string.
127      *       Possible unthrottling events are:
128      *       <ol>
129      *         <li>"WIFI_DISABLE_EVENT": Wifi on to off toggle.
130      *         <li>"APM_DISABLE_EVENT": APM on to off toggle.
131      *         <li>"APM_ENABLE_EVENT": APM off to on toggle.
132      *         <li>"WIFI_AP_CHANGED_EVENT": Wifi is connected to an AP with different SSID.
133      *         <li>"WIFI_CALLING_DISABLE_EVENT": Wifi calling button on to off toggle.
134      *       </ol>
135      *   <li>"NumAttemptsPerFqdn" Integer to specify th count of tunnel setup attempts IWLAN must
136      *       perform with the IP address(es) returned by a single FQDN, before moving on to the next
137      *       FQDN. It is an optional field.
138      *   <li>"HandoverAttemptCount": Integer to specify the number of handover request attempts
139      *       before using initial attach instead. It is an optional field.
140      *       <p>"HandoverAttemptCount" should not be defined in the config when "ErrorType" is
141      *       defined as any other error types except "IKE_PROTOCOL_ERROR_TYPE", including "*".
142      * </ul>
143      *
144      * <p>Note: When the value is "*" for any of "ApnName" or "ErrorType" or "ErrorDetails", it
145      * means that the config definition applies to rest of the errors for which the config is not
146      * defined. For example, if "ApnName" is "ims" and one of the "ErrorType" in it is defined as
147      * "*" - this policy will be applied to the error that doesn't fall into other error types
148      * defined under "ims".
149      */
150     public static final String KEY_ERROR_POLICY_CONFIG_STRING =
151             PREFIX + "key_error_policy_config_string";
152 
153     /**
154      * Default delay in seconds for releasing the IWLAN connection after a WWAN handover. This is
155      * the default value for {@link #KEY_HANDOVER_TO_WWAN_RELEASE_DELAY_SECOND_INT}.
156      */
157     public static final int DEFAULT_HANDOVER_TO_WWAN_RELEASE_DELAY_SECOND_INT = 0;
158 
159     /**
160      * The default value for determining whether the IKE N1_MODE_CAPABILITY Notify payload is
161      * excluded during emergency session setup.
162      */
163     public static final boolean DEFAULT_N1_MODE_EXCLUSION_FOR_EMERGENCY_SESSION_BOOL = false;
164 
165     /**
166      * The default value for determining whether N1 mode shall be enabled or disabled depending on
167      * 5G enabling status via the UI/UX.
168      */
169     public static final boolean DEFAULT_UPDATE_N1_MODE_ON_UI_CHANGE_BOOL = false;
170 
171     /** This is the default value for {@link #KEY_DISTINCT_EPDG_FOR_EMERGENCY_ALLOWED_BOOL}. */
172     public static final boolean DEFAULT_DISTINCT_EPDG_FOR_EMERGENCY_ALLOWED_BOOL = false;
173     /**
174      * Default value indicating whether the UE includes the IKE DEVICE_IDENTITY Notify payload upon
175      * receiving a request. This is the default setting for {@link
176      * #KEY_IKE_DEVICE_IDENTITY_SUPPORTED_BOOL}.
177      */
178     public static final boolean DEFAULT_IKE_DEVICE_IDENTITY_SUPPORTED_BOOL = false;
179 
180     /** This is the default value for {@link #KEY_IKE_SA_TRANSFORMS_REORDER_BOOL}. */
181     public static final boolean DEFAULT_IKE_SA_TRANSFORMS_REORDER_BOOL = false;
182 
183     /**
184      * The default value for determining IWLAN's behavior when error happens during ePDG tunnel
185      * setup. This is the default value for {@link #KEY_ERROR_POLICY_CONFIG_STRING}.
186      */
187     public static final String DEFAULT_ERROR_POLICY_CONFIG_STRING =
188             """
189             [{
190             "ApnName": "*",
191             "ErrorTypes": [{
192                 "ErrorType": "*",
193                 "ErrorDetails": ["*"],
194                 "RetryArray": ["1","2","2","10","20","40","80","160",
195                                 "320","640","1280","1800","3600","-1"],
196                 "UnthrottlingEvents": ["APM_ENABLE_EVENT","APM_DISABLE_EVENT",
197                                         "WIFI_DISABLE_EVENT","WIFI_AP_CHANGED_EVENT"]},{
198                 "ErrorType": "GENERIC_ERROR_TYPE",
199                 "ErrorDetails": ["IO_EXCEPTION"],
200                 "RetryArray": ["0","0","0","30","60+r15","120","-1"],
201                 "UnthrottlingEvents": ["APM_ENABLE_EVENT","APM_DISABLE_EVENT",
202                                         "WIFI_DISABLE_EVENT","WIFI_AP_CHANGED_EVENT"]},{
203                 "ErrorType": "IKE_PROTOCOL_ERROR_TYPE",
204                 "ErrorDetails": ["*"],
205                 "RetryArray": ["5","10","10","20","40","80","160",
206                                 "320","640","1280","1800","3600","-1"],
207                 "UnthrottlingEvents": ["APM_ENABLE_EVENT","WIFI_DISABLE_EVENT",
208                                         "WIFI_CALLING_DISABLE_EVENT"]},{
209                 "ErrorType": "IKE_PROTOCOL_ERROR_TYPE",
210                 "ErrorDetails": ["36"],
211                 "RetryArray": ["0","0","0","10","20","40","80","160",
212                                 "320","640","1280","1800","3600","-1"],
213                 "UnthrottlingEvents": ["APM_ENABLE_EVENT","WIFI_DISABLE_EVENT",
214                                         "WIFI_CALLING_DISABLE_EVENT"],
215                 "HandoverAttemptCount": "3"}]
216             }]
217             """;
218 
219     private static final PersistableBundle sTestBundle = new PersistableBundle();
220 
221     private static PersistableBundle sHiddenBundle = new PersistableBundle();
222 
223     static {
224         sHiddenBundle = createHiddenDefaultConfig();
225     }
226 
227     /**
228      * Creates a hidden default configuration.
229      *
230      * @return a PersistableBundle containing the hidden default configuration
231      */
createHiddenDefaultConfig()232     private static @NonNull PersistableBundle createHiddenDefaultConfig() {
233         PersistableBundle bundle = new PersistableBundle();
234         bundle.putInt(
235                 KEY_HANDOVER_TO_WWAN_RELEASE_DELAY_SECOND_INT,
236                 DEFAULT_HANDOVER_TO_WWAN_RELEASE_DELAY_SECOND_INT);
237         bundle.putBoolean(
238                 KEY_N1_MODE_EXCLUSION_FOR_EMERGENCY_SESSION_BOOL,
239                 DEFAULT_N1_MODE_EXCLUSION_FOR_EMERGENCY_SESSION_BOOL);
240         bundle.putBoolean(
241                 KEY_UPDATE_N1_MODE_ON_UI_CHANGE_BOOL, DEFAULT_UPDATE_N1_MODE_ON_UI_CHANGE_BOOL);
242         bundle.putBoolean(
243                 KEY_DISTINCT_EPDG_FOR_EMERGENCY_ALLOWED_BOOL,
244                 DEFAULT_DISTINCT_EPDG_FOR_EMERGENCY_ALLOWED_BOOL);
245         bundle.putBoolean(
246                 KEY_IKE_DEVICE_IDENTITY_SUPPORTED_BOOL, DEFAULT_IKE_DEVICE_IDENTITY_SUPPORTED_BOOL);
247         bundle.putBoolean(
248                 KEY_IKE_SA_TRANSFORMS_REORDER_BOOL, DEFAULT_IKE_SA_TRANSFORMS_REORDER_BOOL);
249         bundle.putString(KEY_ERROR_POLICY_CONFIG_STRING, DEFAULT_ERROR_POLICY_CONFIG_STRING);
250         return bundle;
251     }
252 
getConfig(Context context, int slotId, String key)253     private static PersistableBundle getConfig(Context context, int slotId, String key) {
254         if (sTestBundle.containsKey(key)) {
255             return sTestBundle;
256         }
257 
258         CarrierConfigManager carrierConfigManager =
259                 context.getSystemService(CarrierConfigManager.class);
260         if (carrierConfigManager == null) {
261             return getDefaultConfig(key);
262         }
263 
264         int subId = IwlanHelper.getSubId(context, slotId);
265         PersistableBundle bundle = carrierConfigManager.getConfigForSubId(subId, key);
266         return bundle.containsKey(key) ? bundle : getDefaultConfig(key);
267     }
268 
getDefaultConfig(String key)269     private static PersistableBundle getDefaultConfig(String key) {
270         PersistableBundle bundle = CarrierConfigManager.getDefaultConfig();
271         if (bundle.containsKey(key)) {
272             return bundle;
273         }
274 
275         if (sHiddenBundle.containsKey(key)) {
276             return sHiddenBundle;
277         }
278 
279         throw new IllegalArgumentException("Default config not found for key: " + key);
280     }
281 
282     /**
283      * Gets a configuration int value for a given slot ID and key.
284      *
285      * @param context the application context
286      * @param slotId the slot ID
287      * @param key the configuration key
288      * @return the configuration int value
289      */
getConfigInt(Context context, int slotId, String key)290     public static int getConfigInt(Context context, int slotId, String key) {
291         return getConfig(context, slotId, key).getInt(key);
292     }
293 
294     /**
295      * Gets a configuration long value for a given slot ID and key.
296      *
297      * @param context the application context
298      * @param slotId the slot ID
299      * @param key the configuration key
300      * @return the configuration long value
301      */
getConfigLong(Context context, int slotId, String key)302     public static long getConfigLong(Context context, int slotId, String key) {
303         return getConfig(context, slotId, key).getLong(key);
304     }
305 
306     /**
307      * Gets a configuration double value for a given slot ID and key.
308      *
309      * @param context the application context
310      * @param slotId the slot ID
311      * @param key the configuration key
312      * @return the configuration double value
313      */
getConfigDouble(Context context, int slotId, String key)314     public static double getConfigDouble(Context context, int slotId, String key) {
315         return getConfig(context, slotId, key).getDouble(key);
316     }
317 
318     /**
319      * Gets a configuration boolean value for a given slot ID and key.
320      *
321      * @param context the application context
322      * @param slotId the slot ID
323      * @param key the configuration key
324      * @return the configuration boolean value
325      */
getConfigBoolean(Context context, int slotId, String key)326     public static boolean getConfigBoolean(Context context, int slotId, String key) {
327         return getConfig(context, slotId, key).getBoolean(key);
328     }
329 
330     /**
331      * Gets a configuration string value for a given slot ID and key.
332      *
333      * @param context the application context
334      * @param slotId the slot ID
335      * @param key the configuration key
336      * @return the configuration string value
337      */
getConfigString(Context context, int slotId, String key)338     public static String getConfigString(Context context, int slotId, String key) {
339         return getConfig(context, slotId, key).getString(key);
340     }
341 
342     /**
343      * Gets a configuration int[] value for a given slot ID and key.
344      *
345      * @param context the application context
346      * @param slotId the slot ID
347      * @param key the configuration key
348      * @return the configuration int[] value
349      */
getConfigIntArray(Context context, int slotId, String key)350     public static int[] getConfigIntArray(Context context, int slotId, String key) {
351         return getConfig(context, slotId, key).getIntArray(key);
352     }
353 
354     /**
355      * Gets a configuration long[] value for a given slot ID and key.
356      *
357      * @param context the application context
358      * @param slotId the slot ID
359      * @param key the configuration key
360      * @return the configuration long[] value
361      */
getConfigLongArray(Context context, int slotId, String key)362     public static long[] getConfigLongArray(Context context, int slotId, String key) {
363         return getConfig(context, slotId, key).getLongArray(key);
364     }
365 
366     /**
367      * Gets a configuration double[] value for a given slot ID and key.
368      *
369      * @param context the application context
370      * @param slotId the slot ID
371      * @param key the configuration key
372      * @return the configuration double[] value
373      */
getConfigDoubleArray(Context context, int slotId, String key)374     public static double[] getConfigDoubleArray(Context context, int slotId, String key) {
375         return getConfig(context, slotId, key).getDoubleArray(key);
376     }
377 
378     /**
379      * Gets a configuration boolean[] value for a given slot ID and key.
380      *
381      * @param context the application context
382      * @param slotId the slot ID
383      * @param key the configuration key
384      * @return the configuration boolean[] value
385      */
getConfigBooleanArray(Context context, int slotId, String key)386     public static boolean[] getConfigBooleanArray(Context context, int slotId, String key) {
387         return getConfig(context, slotId, key).getBooleanArray(key);
388     }
389 
390     /**
391      * Gets a configuration string[] value for a given slot ID and key.
392      *
393      * @param context the application context
394      * @param slotId the slot ID
395      * @param key the configuration key
396      * @return the configuration string[] value
397      */
getConfigStringArray(Context context, int slotId, String key)398     public static String[] getConfigStringArray(Context context, int slotId, String key) {
399         return getConfig(context, slotId, key).getStringArray(key);
400     }
401 
402     /**
403      * Gets the default configuration int value for a given key.
404      *
405      * @param key the configuration key
406      * @return the default configuration int value
407      * @throws IllegalArgumentException if the default configuration is null for the given key
408      */
getDefaultConfigInt(String key)409     public static int getDefaultConfigInt(String key) {
410         return getDefaultConfig(key).getInt(key);
411     }
412 
413     /**
414      * Gets the default configuration long value for a given key.
415      *
416      * @param key the configuration key
417      * @return the default configuration long value
418      * @throws IllegalArgumentException if the default configuration is null for the given key
419      */
getDefaultConfigLong(String key)420     public static long getDefaultConfigLong(String key) {
421         return getDefaultConfig(key).getLong(key);
422     }
423 
424     /**
425      * Gets the default configuration double value for a given key.
426      *
427      * @param key the configuration key
428      * @return the default configuration double value
429      * @throws IllegalArgumentException if the default configuration is null for the given key
430      */
getDefaultConfigDouble(String key)431     public static double getDefaultConfigDouble(String key) {
432         return getDefaultConfig(key).getDouble(key);
433     }
434 
435     /**
436      * Gets the default configuration string value for a given key.
437      *
438      * @param key the configuration key
439      * @return the default configuration string value
440      * @throws IllegalArgumentException if the default configuration is null for the given key
441      */
getDefaultConfigString(String key)442     public static String getDefaultConfigString(String key) {
443         return getDefaultConfig(key).getString(key);
444     }
445 
446     /**
447      * Gets the default configuration boolean value for a given key.
448      *
449      * @param key the configuration key
450      * @return the default configuration boolean value
451      * @throws IllegalArgumentException if the default configuration is null for the given key
452      */
getDefaultConfigBoolean(String key)453     public static boolean getDefaultConfigBoolean(String key) {
454         return getDefaultConfig(key).getBoolean(key);
455     }
456 
457     /**
458      * Gets the default configuration int[] value for a given key.
459      *
460      * @param key the configuration key
461      * @return the default configuration int[] value
462      * @throws IllegalArgumentException if the default configuration is null for the given key
463      */
getDefaultConfigIntArray(String key)464     public static int[] getDefaultConfigIntArray(String key) {
465         return getDefaultConfig(key).getIntArray(key);
466     }
467 
468     /**
469      * Gets the default configuration long value for a given key.
470      *
471      * @param key the configuration key
472      * @return the default configuration long value
473      * @throws IllegalArgumentException if the default configuration is null for the given key
474      */
getDefaultConfigLongArray(String key)475     public static long[] getDefaultConfigLongArray(String key) {
476         return getDefaultConfig(key).getLongArray(key);
477     }
478 
479     /**
480      * Gets the default configuration double[] value for a given key.
481      *
482      * @param key the configuration key
483      * @return the default configuration double[] value
484      * @throws IllegalArgumentException if the default configuration is null for the given key
485      */
getDefaultConfigDoubleArray(String key)486     public static double[] getDefaultConfigDoubleArray(String key) {
487         return getDefaultConfig(key).getDoubleArray(key);
488     }
489 
490     /**
491      * Gets the default configuration string[] value for a given key.
492      *
493      * @param key the configuration key
494      * @return the default configuration string[] value
495      * @throws IllegalArgumentException if the default configuration is null for the given key
496      */
getDefaultConfigStringArray(String key)497     public static String[] getDefaultConfigStringArray(String key) {
498         return getDefaultConfig(key).getStringArray(key);
499     }
500 
501     /**
502      * Gets the default configuration boolean[] value for a given key.
503      *
504      * @param key the configuration key
505      * @return the default configuration boolean[] value
506      * @throws IllegalArgumentException if the default configuration is null for the given key
507      */
getDefaultConfigBooleanArray(String key)508     public static boolean[] getDefaultConfigBooleanArray(String key) {
509         return getDefaultConfig(key).getBooleanArray(key);
510     }
511 
512     @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
putTestConfigBundle(PersistableBundle bundle)513     public static void putTestConfigBundle(PersistableBundle bundle) {
514         sTestBundle.putAll(bundle);
515     }
516 
517     @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
putTestConfigInt(@onNull String key, int value)518     public static void putTestConfigInt(@NonNull String key, int value) {
519         sTestBundle.putInt(key, value);
520     }
521 
522     @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
putTestConfigLong(@onNull String key, long value)523     public static void putTestConfigLong(@NonNull String key, long value) {
524         sTestBundle.putLong(key, value);
525     }
526 
527     @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
putTestConfigDouble(@onNull String key, double value)528     public static void putTestConfigDouble(@NonNull String key, double value) {
529         sTestBundle.putDouble(key, value);
530     }
531 
532     @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
putTestConfigBoolean(@onNull String key, boolean value)533     public static void putTestConfigBoolean(@NonNull String key, boolean value) {
534         sTestBundle.putBoolean(key, value);
535     }
536 
537     @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
putTestConfigString(@onNull String key, String value)538     public static void putTestConfigString(@NonNull String key, String value) {
539         sTestBundle.putString(key, value);
540     }
541 
542     @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
putTestConfigIntArray(@onNull String key, @NonNull int[] value)543     public static void putTestConfigIntArray(@NonNull String key, @NonNull int[] value) {
544         sTestBundle.putIntArray(key, value);
545     }
546 
547     @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
putTestConfigLongArray(@onNull String key, @NonNull long[] value)548     public static void putTestConfigLongArray(@NonNull String key, @NonNull long[] value) {
549         sTestBundle.putLongArray(key, value);
550     }
551 
552     @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
putTestConfigDoubleArray(@onNull String key, @NonNull double[] value)553     public static void putTestConfigDoubleArray(@NonNull String key, @NonNull double[] value) {
554         sTestBundle.putDoubleArray(key, value);
555     }
556 
557     @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
putTestConfigBooleanArray(@onNull String key, @NonNull boolean[] value)558     public static void putTestConfigBooleanArray(@NonNull String key, @NonNull boolean[] value) {
559         sTestBundle.putBooleanArray(key, value);
560     }
561 
562     @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
putTestConfigStringArray(@onNull String key, @NonNull String[] value)563     public static void putTestConfigStringArray(@NonNull String key, @NonNull String[] value) {
564         sTestBundle.putStringArray(key, value);
565     }
566 
567     @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
resetTestConfig()568     public static void resetTestConfig() {
569         sTestBundle.clear();
570     }
571 }
572