• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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