1# Copyright 2014 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import logging 6 7logger = logging.getLogger(__name__) 8 9_LOCK_SCREEN_SETTINGS_PATH = '/data/system/locksettings.db' 10_ALTERNATE_LOCK_SCREEN_SETTINGS_PATH = ( 11 '/data/data/com.android.providers.settings/databases/settings.db') 12PASSWORD_QUALITY_UNSPECIFIED = '0' 13_COMPATIBLE_BUILD_TYPES = ['userdebug', 'eng'] 14 15 16ENABLE_LOCATION_SETTINGS = [ 17 # Note that setting these in this order is required in order for all of 18 # them to take and stick through a reboot. 19 ('com.google.settings/partner', [ 20 ('use_location_for_services', 1), 21 ]), 22 ('settings/secure', [ 23 # Ensure Geolocation is enabled and allowed for tests. 24 ('location_providers_allowed', 'gps,network'), 25 ]), 26 ('com.google.settings/partner', [ 27 ('network_location_opt_in', 1), 28 ]) 29] 30 31DISABLE_LOCATION_SETTINGS = [ 32 ('com.google.settings/partner', [ 33 ('use_location_for_services', 0), 34 ]), 35 ('settings/secure', [ 36 # Ensure Geolocation is disabled. 37 ('location_providers_allowed', ''), 38 ]), 39] 40 41ENABLE_MOCK_LOCATION_SETTINGS = [ 42 ('settings/secure', [ 43 ('mock_location', 1), 44 ]), 45] 46 47DISABLE_MOCK_LOCATION_SETTINGS = [ 48 ('settings/secure', [ 49 ('mock_location', 0), 50 ]), 51] 52 53DETERMINISTIC_DEVICE_SETTINGS = [ 54 ('settings/global', [ 55 ('assisted_gps_enabled', 0), 56 57 # Disable "auto time" and "auto time zone" to avoid network-provided time 58 # to overwrite the device's datetime and timezone synchronized from host 59 # when running tests later. See b/6569849. 60 ('auto_time', 0), 61 ('auto_time_zone', 0), 62 63 ('development_settings_enabled', 1), 64 65 # Flag for allowing ActivityManagerService to send ACTION_APP_ERROR intents 66 # on application crashes and ANRs. If this is disabled, the crash/ANR dialog 67 # will never display the "Report" button. 68 # Type: int ( 0 = disallow, 1 = allow ) 69 ('send_action_app_error', 0), 70 71 ('stay_on_while_plugged_in', 3), 72 73 ('verifier_verify_adb_installs', 0), 74 75 ('window_animation_scale', 0), 76 ]), 77 ('settings/secure', [ 78 ('allowed_geolocation_origins', 79 'http://www.google.co.uk http://www.google.com'), 80 81 # Ensure that we never get random dialogs like "Unfortunately the process 82 # android.process.acore has stopped", which steal the focus, and make our 83 # automation fail (because the dialog steals the focus then mistakenly 84 # receives the injected user input events). 85 ('anr_show_background', 0), 86 87 ('lockscreen.disabled', 1), 88 89 ('screensaver_enabled', 0), 90 91 ('skip_first_use_hints', 1), 92 ]), 93 ('settings/system', [ 94 # Don't want devices to accidentally rotate the screen as that could 95 # affect performance measurements. 96 ('accelerometer_rotation', 0), 97 98 ('lockscreen.disabled', 1), 99 100 # Turn down brightness and disable auto-adjust so that devices run cooler. 101 ('screen_brightness', 5), 102 ('screen_brightness_mode', 0), 103 104 ('user_rotation', 0), 105 106 ('window_animation_scale', 0), 107 ]), 108] 109 110NETWORK_DISABLED_SETTINGS = [ 111 ('settings/global', [ 112 ('airplane_mode_on', 1), 113 ('wifi_on', 0), 114 ]), 115] 116 117 118class ContentSettings(dict): 119 120 """A dict interface to interact with device content settings. 121 122 System properties are key/value pairs as exposed by adb shell content. 123 """ 124 125 def __init__(self, table, device): 126 super(ContentSettings, self).__init__() 127 self._table = table 128 self._device = device 129 130 @staticmethod 131 def _GetTypeBinding(value): 132 if isinstance(value, bool): 133 return 'b' 134 if isinstance(value, float): 135 return 'f' 136 if isinstance(value, int): 137 return 'i' 138 if isinstance(value, long): 139 return 'l' 140 if isinstance(value, str): 141 return 's' 142 raise ValueError('Unsupported type %s' % type(value)) 143 144 def iteritems(self): 145 for row in self._device.RunShellCommand( 146 ['content', 'query', '--uri', 'content://%s' % self._table], 147 check_return=True, as_root=True): 148 key, value = _ParseContentRow(row) 149 if not key: 150 continue 151 yield key, value 152 153 def __getitem__(self, key): 154 query_row = self._device.RunShellCommand( 155 ['content', 'query', '--uri', 'content://%s' % self._table, 156 '--where', "name='%s'" % key], 157 check_return=True, as_root=True, single_line=True) 158 parsed_key, parsed_value = _ParseContentRow(query_row) 159 if parsed_key is None: 160 raise KeyError('key=%s not found' % key) 161 if parsed_key != key: 162 raise KeyError('Expected key=%s, but got key=%s' % (key, parsed_key)) 163 return parsed_value 164 165 def __setitem__(self, key, value): 166 if key in self: 167 self._device.RunShellCommand( 168 ['content', 'update', '--uri', 'content://%s' % self._table, 169 '--bind', 'value:%s:%s' % (self._GetTypeBinding(value), value), 170 '--where', "name='%s'" % key], 171 check_return=True, as_root=True) 172 else: 173 self._device.RunShellCommand( 174 ['content', 'insert', '--uri', 'content://%s' % self._table, 175 '--bind', 'name:%s:%s' % (self._GetTypeBinding(key), key), 176 '--bind', 'value:%s:%s' % (self._GetTypeBinding(value), value)], 177 check_return=True, as_root=True) 178 179 def __delitem__(self, key): 180 self._device.RunShellCommand( 181 ['content', 'delete', '--uri', 'content://%s' % self._table, 182 '--bind', 'name:%s:%s' % (self._GetTypeBinding(key), key)], 183 check_return=True, as_root=True) 184 185 186def ConfigureContentSettings(device, desired_settings): 187 """Configures device content setings from a list. 188 189 Many settings are documented at: 190 http://developer.android.com/reference/android/provider/Settings.Global.html 191 http://developer.android.com/reference/android/provider/Settings.Secure.html 192 http://developer.android.com/reference/android/provider/Settings.System.html 193 194 Many others are undocumented. 195 196 Args: 197 device: A DeviceUtils instance for the device to configure. 198 desired_settings: A list of (table, [(key: value), ...]) for all 199 settings to configure. 200 """ 201 for table, key_value in desired_settings: 202 settings = ContentSettings(table, device) 203 for key, value in key_value: 204 settings[key] = value 205 logger.info('\n%s %s', table, (80 - len(table)) * '-') 206 for key, value in sorted(settings.iteritems()): 207 logger.info('\t%s: %s', key, value) 208 209 210def SetLockScreenSettings(device): 211 """Sets lock screen settings on the device. 212 213 On certain device/Android configurations we need to disable the lock screen in 214 a different database. Additionally, the password type must be set to 215 DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED. 216 Lock screen settings are stored in sqlite on the device in: 217 /data/system/locksettings.db 218 219 IMPORTANT: The first column is used as a primary key so that all rows with the 220 same value for that column are removed from the table prior to inserting the 221 new values. 222 223 Args: 224 device: A DeviceUtils instance for the device to configure. 225 226 Raises: 227 Exception if the setting was not properly set. 228 """ 229 if device.build_type not in _COMPATIBLE_BUILD_TYPES: 230 logger.warning('Unable to disable lockscreen on %s builds.', 231 device.build_type) 232 return 233 234 def get_lock_settings(table): 235 return [(table, 'lockscreen.disabled', '1'), 236 (table, 'lockscreen.password_type', PASSWORD_QUALITY_UNSPECIFIED), 237 (table, 'lockscreen.password_type_alternate', 238 PASSWORD_QUALITY_UNSPECIFIED)] 239 240 if device.FileExists(_LOCK_SCREEN_SETTINGS_PATH): 241 db = _LOCK_SCREEN_SETTINGS_PATH 242 locksettings = get_lock_settings('locksettings') 243 columns = ['name', 'user', 'value'] 244 generate_values = lambda k, v: [k, '0', v] 245 elif device.FileExists(_ALTERNATE_LOCK_SCREEN_SETTINGS_PATH): 246 db = _ALTERNATE_LOCK_SCREEN_SETTINGS_PATH 247 locksettings = get_lock_settings('secure') + get_lock_settings('system') 248 columns = ['name', 'value'] 249 generate_values = lambda k, v: [k, v] 250 else: 251 logger.warning('Unable to find database file to set lock screen settings.') 252 return 253 254 for table, key, value in locksettings: 255 # Set the lockscreen setting for default user '0' 256 values = generate_values(key, value) 257 258 cmd = """begin transaction; 259delete from '%(table)s' where %(primary_key)s='%(primary_value)s'; 260insert into '%(table)s' (%(columns)s) values (%(values)s); 261commit transaction;""" % { 262 'table': table, 263 'primary_key': columns[0], 264 'primary_value': values[0], 265 'columns': ', '.join(columns), 266 'values': ', '.join(["'%s'" % value for value in values]) 267 } 268 output_msg = device.RunShellCommand( 269 ['sqlite3', db, cmd], check_return=True, as_root=True) 270 if output_msg: 271 logger.info(' '.join(output_msg)) 272 273 274def _ParseContentRow(row): 275 """Parse key, value entries from a row string.""" 276 # Example row: 277 # 'Row: 0 _id=13, name=logging_id2, value=-1fccbaa546705b05' 278 fields = row.split(', ') 279 key = None 280 value = '' 281 for field in fields: 282 k, _, v = field.partition('=') 283 if k == 'name': 284 key = v 285 elif k == 'value': 286 value = v 287 return key, value 288