Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1247)

Side by Side Diff: chrome/tools/test/experiment_tool_win.py

Issue 2933043002: Installer support for Windows 10 inactive user toast. (Closed)
Patch Set: added --experiment-enable-for-testing switch for testing Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 # Copyright 2017 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 # A tool for manipulating the user retention experiment state for a Chrome
7 # install.
8 #
9 # usage: experiment_tool_win.py [-h] --operation {clean,prep}
10 # [--channel {stable,beta,dev,canary}]
11 # [--system-level]
12 #
13 # A helper for working with state associated with the M60 Chrome on Windows 10
14 # retention experiment.
Nico 2017/06/15 14:47:37 I love this descriptive comment up here. And the w
grt (UTC plus 2) 2017/06/15 15:07:26 Nice!
15 #
16 # optional arguments:
17 # -h, --help show this help message and exit
18 # --operation {clean,prep}
19 # The operation to be performed.
20 # --channel {stable,beta,dev,canary}
21 # The install on which to operate (stable by default).
22 # --system-level Specify to operate on a system-level install (user-
23 # level by default).
24
25 import argparse
26 import math
27 import sys
28 from datetime import datetime, timedelta
29 import win32api
30 import win32security
31 import _winreg
32
33
34 def GetUserSidString():
35 """Returns the current user's SID string."""
36 token_handle = win32security.OpenProcessToken(win32api.GetCurrentProcess(),
37 win32security.TOKEN_QUERY)
38 user_sid, _ = win32security.GetTokenInformation(token_handle,
39 win32security.TokenUser)
40 return win32security.ConvertSidToStringSid(user_sid)
41
42
43 def InternalTimeFromPyTime(pytime):
44 """Returns a Chromium internal time value representing a Python datetime."""
45 # Microseconds since 1601-01-01 00:00:00 UTC
46 delta = pytime - datetime(1601, 1, 1)
47 return math.trunc(delta.total_seconds()) * 1000000 + delta.microseconds
48
49
50 class ChromeState:
51 """An object that provides mutations on Chrome's state relating to the
52 user experiment.
53 """
54 _CHANNEL_CONFIGS = {
55 'stable': {
56 'guid': '{8A69D345-D564-463c-AFF1-A69D9E530F96}'
57 },
58 'beta': {
59 'guid': '{8237E44A-0054-442C-B6B6-EA0509993955}'
60 },
61 'dev': {
62 'guid': '{401C381F-E0DE-4B85-8BD8-3F3F14FBDA57}'
63 },
64 'canary': {
65 'guid': '{4ea16ac7-fd5a-47c3-875b-dbf4a2008c20}'
66 },
67 }
68 _GOOGLE_UPDATE_PATH = 'Software\\Google\\Update'
69 _ACTIVE_SETUP_PATH = 'Software\\Microsoft\\Active Setup\\Installed ' + \
70 'Components\\'
71
72 def __init__(self, channel_name, system_level):
73 self._config = ChromeState._CHANNEL_CONFIGS[channel_name]
74 self._system_level = system_level
75 self._registry_root = _winreg.HKEY_LOCAL_MACHINE if self._system_level \
76 else _winreg.HKEY_CURRENT_USER
77
78 def SetRetentionStudyValue(self, study):
79 """Sets the RetentionStudy value for the install."""
80 path = ChromeState._GOOGLE_UPDATE_PATH + '\\ClientState\\' + \
81 self._config['guid']
82 with _winreg.OpenKey(self._registry_root, path, 0,
83 _winreg.KEY_WOW64_32KEY |
84 _winreg.KEY_SET_VALUE) as key:
85 _winreg.SetValueEx(key, 'RetentionStudy', 0, _winreg.REG_DWORD, study)
86
87 def DeleteRetentionStudyValue(self):
88 """Deletes the RetentionStudy value for the install."""
89 path = ChromeState._GOOGLE_UPDATE_PATH + '\\ClientState\\' + \
90 self._config['guid']
91 try:
92 with _winreg.OpenKey(self._registry_root, path, 0,
93 _winreg.KEY_WOW64_32KEY |
94 _winreg.KEY_SET_VALUE) as key:
95 _winreg.DeleteValue(key, 'RetentionStudy')
96 except WindowsError as error:
97 if error.winerror != 2:
98 raise
99
100 def DeleteExperimentLabelsValue(self):
101 """Deletes the experiment_labels for the install."""
102 medium = 'Medium' if self._system_level else ''
103 path = ChromeState._GOOGLE_UPDATE_PATH + '\\ClientState' + medium + '\\' + \
104 self._config['guid']
105 try:
106 with _winreg.OpenKey(self._registry_root, path, 0,
107 _winreg.KEY_WOW64_32KEY |
108 _winreg.KEY_SET_VALUE) as key:
109 _winreg.DeleteValue(key, 'experiment_labels')
110 except WindowsError as error:
111 if error.winerror != 2:
112 raise
113
114 def DeleteRentionKey(self):
115 """Deletes the Retention key for the current user."""
116 medium = 'Medium' if self._system_level else ''
117 path = ChromeState._GOOGLE_UPDATE_PATH + '\\ClientState' + medium + '\\' + \
118 self._config['guid'] + '\\Retention'
119 try:
120 if self._system_level:
121 _winreg.DeleteKeyEx(self._registry_root,
122 path + '\\' + GetUserSidString(),
123 _winreg.KEY_WOW64_32KEY)
124 _winreg.DeleteKeyEx(self._registry_root, path, _winreg.KEY_WOW64_32KEY)
125 except WindowsError as error:
126 if error.winerror != 2:
127 raise
128
129 def SetLastRunTime(self, delta):
130 """Sets Chrome's lastrun time for the current user."""
131 path = ChromeState._GOOGLE_UPDATE_PATH + '\\ClientState\\' + \
132 self._config['guid']
133 lastrun = InternalTimeFromPyTime(datetime.utcnow() - delta)
134 with _winreg.CreateKeyEx(_winreg.HKEY_CURRENT_USER, path, 0,
135 _winreg.KEY_WOW64_32KEY |
136 _winreg.KEY_SET_VALUE) as key:
137 _winreg.SetValueEx(key, 'lastrun', 0, _winreg.REG_SZ, str(lastrun))
138
139 def AdjustActiveSetupCommand(self):
140 """Adds --experiment-enterprise-bypass to system-level Chrome's Active Setup
141 command."""
142 if not self._system_level:
143 return
144 enable_switch = '--experiment-enable-for-testing'
145 bypass_switch = '--experiment-enterprise-bypass'
146 for flag in [_winreg.KEY_WOW64_32KEY, _winreg.KEY_WOW64_64KEY]:
147 try:
148 with _winreg.OpenKey(self._registry_root,
149 ChromeState._ACTIVE_SETUP_PATH +
150 self._config['guid'], 0,
151 _winreg.KEY_SET_VALUE | _winreg.KEY_QUERY_VALUE |
152 flag) as key:
153 command, _ = _winreg.QueryValueEx(key, 'StubPath')
154 if not bypass_switch in command:
155 command += ' ' + bypass_switch
156 if not enable_switch in command:
157 command += ' ' + enable_switch
158 _winreg.SetValueEx(key, 'StubPath', 0, _winreg.REG_SZ, command)
159 except WindowsError as error:
160 if error.winerror != 2:
161 raise
162
163 def ClearUserActiveSetup(self):
164 """Clears per-user state associated with Active Setup so that it will run
165 again on next login."""
166 if not self._system_level:
167 return
168 paths = [ChromeState._ACTIVE_SETUP_PATH,
169 ChromeState._ACTIVE_SETUP_PATH.replace('Software\\',
170 'Software\\Wow6432Node\\')]
171 for path in paths:
172 try:
173 _winreg.DeleteKeyEx(_winreg.HKEY_CURRENT_USER,
174 path + self._config['guid'], 0)
175 except WindowsError as error:
176 if error.winerror != 2:
177 raise
178
179
180 def DoClean(chrome_state):
181 """Deletes all state associated with the user experiment."""
182 chrome_state.DeleteRetentionStudyValue()
183 chrome_state.DeleteExperimentLabelsValue()
184 chrome_state.DeleteRentionKey()
185 return 0
186
187
188 def DoPrep(chrome_state):
189 """Prepares an install for participation in the experiment."""
190 # Clear old state.
191 DoClean(chrome_state)
192 # Make Chrome appear to have been last run 30 days ago.
193 chrome_state.SetLastRunTime(timedelta(30))
194 # Add the enterprise bypass switch to the Active Setup command.
195 chrome_state.AdjustActiveSetupCommand()
196 # Cause Active Setup to be run for the current user on next logon.
197 chrome_state.ClearUserActiveSetup()
198 # Put the machine into the first study.
199 chrome_state.SetRetentionStudyValue(1)
200 return 0
201
202
203 def main(options):
204 chrome_state = ChromeState(options.channel, options.system_level)
205 if options.operation == 'clean':
206 return DoClean(chrome_state)
207 if options.operation == 'prep':
208 return DoPrep(chrome_state)
209 return 1
210
211
212 if __name__ == '__main__':
213 parser = argparse.ArgumentParser(
214 description='A helper for working with state associated with the M60 '
215 'Chrome on Windows 10 retention experiment.')
216 parser.add_argument('--operation', required=True, choices=['clean', 'prep'],
217 help='The operation to be performed.')
218 parser.add_argument('--channel', default='stable',
219 choices=['stable', 'beta', 'dev', 'canary'],
220 help='The install on which to operate (stable by ' \
221 'default).')
222 parser.add_argument('--system-level', action='store_true',
223 help='Specify to operate on a system-level install ' \
224 '(user-level by default).')
225 sys.exit(main(parser.parse_args()))
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698