1# Copyright 2017 The Chromium Authors 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5"""Utility functions for modifying an app's settings file using JSON.""" 6 7import json 8import logging 9 10 11def UnicodeToStr(data): 12 """Recursively converts any Unicode to Python strings. 13 14 Args: 15 data: The data to be converted. 16 17 Return: 18 A copy of the given data, but with instances of Unicode converted to Python 19 strings. 20 """ 21 if isinstance(data, dict): 22 return { 23 UnicodeToStr(key): UnicodeToStr(value) 24 for key, value in data.items() 25 } 26 if isinstance(data, list): 27 return [UnicodeToStr(element) for element in data] 28 try: 29 # Python-2 compatibility. 30 if isinstance(data, unicode): 31 return data.encode('utf-8') 32 except NameError: 33 # Strings are already unicode in python3. 34 pass 35 return data 36 37 38def ExtractSettingsFromJson(filepath): 39 """Extracts the settings data from the given JSON file. 40 41 Args: 42 filepath: The path to the JSON file to read. 43 44 Return: 45 The data read from the JSON file with strings converted to Python strings. 46 """ 47 # json.load() loads strings as unicode, which causes issues when trying 48 # to edit string values in preference files, so convert to Python strings 49 with open(filepath) as prefs_file: 50 return UnicodeToStr(json.load(prefs_file)) 51 52 53def ApplySharedPreferenceSetting(shared_pref, setting): 54 """Applies the given app settings to the given device. 55 56 Modifies an installed app's settings by modifying its shared preference 57 settings file. Provided settings data must be a settings dictionary, 58 which are in the following format: 59 { 60 "package": "com.example.package", 61 "filename": "AppSettingsFile.xml", 62 "supports_encrypted_path": true, 63 "set": { 64 "SomeBoolToSet": true, 65 "SomeStringToSet": "StringValue", 66 }, 67 "remove": [ 68 "list", 69 "of", 70 "keys", 71 "to", 72 "remove", 73 ] 74 } 75 76 Example JSON files that can be read with ExtractSettingsFromJson and passed to 77 this function are in //chrome/android/shared_preference_files/test/. 78 79 Args: 80 shared_pref: The devil SharedPrefs object for the device the settings will 81 be applied to. 82 setting: A settings dictionary to apply. 83 """ 84 shared_pref.Load() 85 for key in setting.get('remove', []): 86 try: 87 shared_pref.Remove(key) 88 except KeyError: 89 logging.warning("Attempted to remove non-existent key %s", key) 90 for key, value in setting.get('set', {}).items(): 91 is_set = False 92 if not is_set and isinstance(value, bool): 93 shared_pref.SetBoolean(key, value) 94 is_set = True 95 try: 96 # Python-2 compatibility. 97 if not is_set and isinstance(value, basestring): 98 shared_pref.SetString(key, value) 99 is_set = True 100 if not is_set and isinstance(value, (long, int)): 101 shared_pref.SetLong(key, value) 102 is_set = True 103 except NameError: 104 if not is_set and isinstance(value, str): 105 shared_pref.SetString(key, value) 106 is_set = True 107 if not is_set and isinstance(value, int): 108 shared_pref.SetLong(key, value) 109 is_set = True 110 if not is_set and isinstance(value, list): 111 shared_pref.SetStringSet(key, value) 112 is_set = True 113 if not is_set: 114 raise ValueError("Given invalid value type %s for key %s" % ( 115 str(type(value)), key)) 116 shared_pref.Commit() 117