1 /*
2  * Copyright (C) 2016 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.android.server.autofill;
18 
19 import static android.service.autofill.AutofillFieldClassificationService.EXTRA_SCORES;
20 import static android.service.autofill.AutofillService.EXTRA_RESULT;
21 
22 import static com.android.server.autofill.AutofillManagerService.RECEIVER_BUNDLE_EXTRA_SESSIONS;
23 
24 import android.os.Bundle;
25 import android.os.RemoteCallback;
26 import android.os.ShellCommand;
27 import android.os.UserHandle;
28 import android.service.autofill.AutofillFieldClassificationService.Scores;
29 import android.view.autofill.AutofillManager;
30 
31 import com.android.internal.os.IResultReceiver;
32 
33 import java.io.PrintWriter;
34 import java.util.ArrayList;
35 import java.util.concurrent.CountDownLatch;
36 import java.util.concurrent.TimeUnit;
37 
38 public final class AutofillManagerServiceShellCommand extends ShellCommand {
39 
40     private final AutofillManagerService mService;
41 
AutofillManagerServiceShellCommand(AutofillManagerService service)42     public AutofillManagerServiceShellCommand(AutofillManagerService service) {
43         mService = service;
44     }
45 
46     @Override
onCommand(String cmd)47     public int onCommand(String cmd) {
48         if (cmd == null) {
49             return handleDefaultCommands(cmd);
50         }
51         final PrintWriter pw = getOutPrintWriter();
52         switch (cmd) {
53             case "list":
54                 return requestList(pw);
55             case "destroy":
56                 return requestDestroy(pw);
57             case "reset":
58                 return requestReset();
59             case "get":
60                 return requestGet(pw);
61             case "set":
62                 return requestSet(pw);
63             default:
64                 return handleDefaultCommands(cmd);
65         }
66     }
67 
68     @Override
onHelp()69     public void onHelp() {
70         try (final PrintWriter pw = getOutPrintWriter();) {
71             pw.println("AutoFill Service (autofill) commands:");
72             pw.println("  help");
73             pw.println("    Prints this help text.");
74             pw.println("");
75             pw.println("  get log_level ");
76             pw.println("    Gets the Autofill log level (off | debug | verbose).");
77             pw.println("");
78             pw.println("  get max_partitions");
79             pw.println("    Gets the maximum number of partitions per session.");
80             pw.println("");
81             pw.println("  get max_visible_datasets");
82             pw.println("    Gets the maximum number of visible datasets in the UI.");
83             pw.println("");
84             pw.println("  get full_screen_mode");
85             pw.println("    Gets the Fill UI full screen mode");
86             pw.println("");
87             pw.println("  get fc_score [--algorithm ALGORITHM] value1 value2");
88             pw.println("    Gets the field classification score for 2 fields.");
89             pw.println("");
90             pw.println("  get bind-instant-service-allowed");
91             pw.println("    Gets whether binding to services provided by instant apps is allowed");
92             pw.println("");
93             pw.println("  get saved-password-count");
94             pw.println("    Gets the number of saved passwords in the current service.");
95             pw.println("");
96             pw.println("  set log_level [off | debug | verbose]");
97             pw.println("    Sets the Autofill log level.");
98             pw.println("");
99             pw.println("  set max_partitions number");
100             pw.println("    Sets the maximum number of partitions per session.");
101             pw.println("");
102             pw.println("  set max_visible_datasets number");
103             pw.println("    Sets the maximum number of visible datasets in the UI.");
104             pw.println("");
105             pw.println("  set full_screen_mode [true | false | default]");
106             pw.println("    Sets the Fill UI full screen mode");
107             pw.println("");
108             pw.println("  set bind-instant-service-allowed [true | false]");
109             pw.println("    Sets whether binding to services provided by instant apps is allowed");
110             pw.println("");
111             pw.println("  set temporary-augmented-service USER_ID [COMPONENT_NAME DURATION]");
112             pw.println("    Temporarily (for DURATION ms) changes the augmented autofill service "
113                     + "implementation.");
114             pw.println("    To reset, call with just the USER_ID argument.");
115             pw.println("");
116             pw.println("  set default-augmented-service-enabled USER_ID [true|false]");
117             pw.println("    Enable / disable the default augmented autofill service for the user.");
118             pw.println("");
119             pw.println("  set temporary-detection-service USER_ID [COMPONENT_NAME DURATION]");
120             pw.println("    Temporarily (for DURATION ms) changes the autofill detection service "
121                     + "implementation.");
122             pw.println("    To reset, call with [COMPONENT_NAME 0].");
123             pw.println("");
124             pw.println("  get default-augmented-service-enabled USER_ID");
125             pw.println("    Checks whether the default augmented autofill service is enabled for "
126                     + "the user.");
127             pw.println("");
128             pw.println("  list sessions [--user USER_ID]");
129             pw.println("    Lists all pending sessions.");
130             pw.println("");
131             pw.println("  destroy sessions [--user USER_ID]");
132             pw.println("    Destroys all pending sessions.");
133             pw.println("");
134             pw.println("  reset");
135             pw.println("    Resets all pending sessions and cached service connections.");
136             pw.println("");
137         }
138     }
139 
requestGet(PrintWriter pw)140     private int requestGet(PrintWriter pw) {
141         final String what = getNextArgRequired();
142         switch(what) {
143             case "log_level":
144                 return getLogLevel(pw);
145             case "max_partitions":
146                 return getMaxPartitions(pw);
147             case "max_visible_datasets":
148                 return getMaxVisibileDatasets(pw);
149             case "fc_score":
150                 return getFieldClassificationScore(pw);
151             case "full_screen_mode":
152                 return getFullScreenMode(pw);
153             case "bind-instant-service-allowed":
154                 return getBindInstantService(pw);
155             case "default-augmented-service-enabled":
156                 return getDefaultAugmentedServiceEnabled(pw);
157             case "field-detection-service-enabled":
158                 return isFieldDetectionServiceEnabled(pw);
159             case "saved-password-count":
160                 return getSavedPasswordCount(pw);
161             default:
162                 pw.println("Invalid set: " + what);
163                 return -1;
164         }
165     }
166 
requestSet(PrintWriter pw)167     private int requestSet(PrintWriter pw) {
168         final String what = getNextArgRequired();
169 
170         switch(what) {
171             case "log_level":
172                 return setLogLevel(pw);
173             case "max_partitions":
174                 return setMaxPartitions();
175             case "max_visible_datasets":
176                 return setMaxVisibileDatasets();
177             case "full_screen_mode":
178                 return setFullScreenMode(pw);
179             case "bind-instant-service-allowed":
180                 return setBindInstantService(pw);
181             case "temporary-augmented-service":
182                 return setTemporaryAugmentedService(pw);
183             case "default-augmented-service-enabled":
184                 return setDefaultAugmentedServiceEnabled(pw);
185             case "temporary-detection-service":
186                 return setTemporaryDetectionService(pw);
187             default:
188                 pw.println("Invalid set: " + what);
189                 return -1;
190         }
191     }
192 
getLogLevel(PrintWriter pw)193     private int getLogLevel(PrintWriter pw) {
194         final int logLevel = mService.getLogLevel();
195         switch (logLevel) {
196             case AutofillManager.FLAG_ADD_CLIENT_VERBOSE:
197                 pw.println("verbose");
198                 return 0;
199             case AutofillManager.FLAG_ADD_CLIENT_DEBUG:
200                 pw.println("debug");
201                 return 0;
202             case 0:
203                 pw.println("off");
204                 return 0;
205             default:
206                 pw.println("unknow (" + logLevel + ")");
207                 return 0;
208         }
209     }
210 
setLogLevel(PrintWriter pw)211     private int setLogLevel(PrintWriter pw) {
212         final String logLevel = getNextArgRequired();
213         switch (logLevel.toLowerCase()) {
214             case "verbose":
215                 mService.setLogLevel(AutofillManager.FLAG_ADD_CLIENT_VERBOSE);
216                 return 0;
217             case "debug":
218                 mService.setLogLevel(AutofillManager.FLAG_ADD_CLIENT_DEBUG);
219                 return 0;
220             case "off":
221                 mService.setLogLevel(AutofillManager.NO_LOGGING);
222                 return 0;
223             default:
224                 pw.println("Invalid level: " + logLevel);
225                 return -1;
226         }
227     }
228 
getMaxPartitions(PrintWriter pw)229     private int getMaxPartitions(PrintWriter pw) {
230         pw.println(mService.getMaxPartitions());
231         return 0;
232     }
233 
setMaxPartitions()234     private int setMaxPartitions() {
235         mService.setMaxPartitions(Integer.parseInt(getNextArgRequired()));
236         return 0;
237     }
238 
getMaxVisibileDatasets(PrintWriter pw)239     private int getMaxVisibileDatasets(PrintWriter pw) {
240         pw.println(mService.getMaxVisibleDatasets());
241         return 0;
242     }
243 
setMaxVisibileDatasets()244     private int setMaxVisibileDatasets() {
245         mService.setMaxVisibleDatasets(Integer.parseInt(getNextArgRequired()));
246         return 0;
247     }
248 
getFieldClassificationScore(PrintWriter pw)249     private int getFieldClassificationScore(PrintWriter pw) {
250         final String nextArg = getNextArgRequired();
251         final String algorithm, value1;
252         if ("--algorithm".equals(nextArg)) {
253             algorithm = getNextArgRequired();
254             value1 = getNextArgRequired();
255         } else {
256             algorithm = null;
257             value1 = nextArg;
258         }
259         final String value2 = getNextArgRequired();
260 
261         final CountDownLatch latch = new CountDownLatch(1);
262         mService.calculateScore(algorithm, value1, value2, new RemoteCallback((result) -> {
263             final Scores scores = result.getParcelable(EXTRA_SCORES, android.service.autofill.AutofillFieldClassificationService.Scores.class);
264             if (scores == null) {
265                 pw.println("no score");
266             } else {
267                 pw.println(scores.scores[0][0]);
268             }
269             latch.countDown();
270         }));
271 
272         return waitForLatch(pw, latch);
273     }
274 
getFullScreenMode(PrintWriter pw)275     private int getFullScreenMode(PrintWriter pw) {
276         final Boolean mode = mService.getFullScreenMode();
277         if (mode == null) {
278             pw.println("default");
279         } else if (mode) {
280             pw.println("true");
281         } else {
282             pw.println("false");
283         }
284         return 0;
285     }
286 
setFullScreenMode(PrintWriter pw)287     private int setFullScreenMode(PrintWriter pw) {
288         final String mode = getNextArgRequired();
289         switch (mode.toLowerCase()) {
290             case "true":
291                 mService.setFullScreenMode(Boolean.TRUE);
292                 return 0;
293             case "false":
294                 mService.setFullScreenMode(Boolean.FALSE);
295                 return 0;
296             case "default":
297                 mService.setFullScreenMode(null);
298                 return 0;
299             default:
300                 pw.println("Invalid mode: " + mode);
301                 return -1;
302         }
303     }
304 
getBindInstantService(PrintWriter pw)305     private int getBindInstantService(PrintWriter pw) {
306         if (mService.getAllowInstantService()) {
307             pw.println("true");
308         } else {
309             pw.println("false");
310         }
311         return 0;
312     }
313 
setBindInstantService(PrintWriter pw)314     private int setBindInstantService(PrintWriter pw) {
315         final String mode = getNextArgRequired();
316         switch (mode.toLowerCase()) {
317             case "true":
318                 mService.setAllowInstantService(true);
319                 return 0;
320             case "false":
321                 mService.setAllowInstantService(false);
322                 return 0;
323             default:
324                 pw.println("Invalid mode: " + mode);
325                 return -1;
326         }
327     }
328 
setTemporaryDetectionService(PrintWriter pw)329     private int setTemporaryDetectionService(PrintWriter pw) {
330         final int userId = getNextIntArgRequired();
331         final String serviceName = getNextArg();
332         if (serviceName == null) {
333             mService.resetTemporaryDetectionService(userId);
334             return 0;
335         }
336         final int duration = getNextIntArgRequired();
337         if (duration <= 0) {
338             mService.resetTemporaryDetectionService(userId);
339             return 0;
340         }
341 
342         mService.setTemporaryDetectionService(userId, serviceName, duration);
343         pw.println("Autofill Detection Service temporarily set to " + serviceName + " for "
344                 + duration + "ms");
345         return 0;
346     }
347 
isFieldDetectionServiceEnabled(PrintWriter pw)348     private int isFieldDetectionServiceEnabled(PrintWriter pw) {
349         final int userId = getNextIntArgRequired();
350         boolean enabled = mService.isFieldDetectionServiceEnabledForUser(userId);
351         pw.println(enabled);
352         return 0;
353     }
354 
setTemporaryAugmentedService(PrintWriter pw)355     private int setTemporaryAugmentedService(PrintWriter pw) {
356         final int userId = getNextIntArgRequired();
357         final String serviceName = getNextArg();
358         if (serviceName == null) {
359             mService.resetTemporaryAugmentedAutofillService(userId);
360             return 0;
361         }
362         final int duration = getNextIntArgRequired();
363         mService.setTemporaryAugmentedAutofillService(userId, serviceName, duration);
364         pw.println("AugmentedAutofillService temporarily set to " + serviceName + " for "
365                 + duration + "ms");
366         return 0;
367     }
368 
getDefaultAugmentedServiceEnabled(PrintWriter pw)369     private int getDefaultAugmentedServiceEnabled(PrintWriter pw) {
370         final int userId = getNextIntArgRequired();
371         final boolean enabled = mService.isDefaultAugmentedServiceEnabled(userId);
372         pw.println(enabled);
373         return 0;
374     }
375 
setDefaultAugmentedServiceEnabled(PrintWriter pw)376     private int setDefaultAugmentedServiceEnabled(PrintWriter pw) {
377         final int userId = getNextIntArgRequired();
378         final boolean enabled = Boolean.parseBoolean(getNextArgRequired());
379         final boolean changed = mService.setDefaultAugmentedServiceEnabled(userId, enabled);
380         if (!changed) {
381             pw.println("already " + enabled);
382         }
383         return 0;
384     }
385 
getSavedPasswordCount(PrintWriter pw)386     private int getSavedPasswordCount(PrintWriter pw) {
387         final int userId = getNextIntArgRequired();
388         CountDownLatch latch = new CountDownLatch(1);
389         IResultReceiver resultReceiver = new IResultReceiver.Stub() {
390             @Override
391             public void send(int resultCode, Bundle resultData) {
392                 pw.println("resultCode=" + resultCode);
393                 if (resultCode == 0 && resultData != null) {
394                     pw.println("value=" + resultData.getInt(EXTRA_RESULT));
395                 }
396                 latch.countDown();
397             }
398         };
399         if (mService.requestSavedPasswordCount(userId, resultReceiver)) {
400             waitForLatch(pw, latch);
401         }
402         return 0;
403     }
404 
requestDestroy(PrintWriter pw)405     private int requestDestroy(PrintWriter pw) {
406         if (!isNextArgSessions(pw)) {
407             return -1;
408         }
409 
410         final int userId = getUserIdFromArgsOrAllUsers();
411         final CountDownLatch latch = new CountDownLatch(1);
412         final IResultReceiver receiver = new IResultReceiver.Stub() {
413             @Override
414             public void send(int resultCode, Bundle resultData) {
415                 latch.countDown();
416             }
417         };
418         return requestSessionCommon(pw, latch, () -> mService.removeAllSessions(userId, receiver));
419     }
420 
requestList(PrintWriter pw)421     private int requestList(PrintWriter pw) {
422         if (!isNextArgSessions(pw)) {
423             return -1;
424         }
425 
426         final int userId = getUserIdFromArgsOrAllUsers();
427         final CountDownLatch latch = new CountDownLatch(1);
428         final IResultReceiver receiver = new IResultReceiver.Stub() {
429             @Override
430             public void send(int resultCode, Bundle resultData) {
431                 final ArrayList<String> sessions = resultData
432                         .getStringArrayList(RECEIVER_BUNDLE_EXTRA_SESSIONS);
433                 for (String session : sessions) {
434                     pw.println(session);
435                 }
436                 latch.countDown();
437             }
438         };
439         return requestSessionCommon(pw, latch, () -> mService.listSessions(userId, receiver));
440     }
441 
isNextArgSessions(PrintWriter pw)442     private boolean isNextArgSessions(PrintWriter pw) {
443         final String type = getNextArgRequired();
444         if (!type.equals("sessions")) {
445             pw.println("Error: invalid list type");
446             return false;
447         }
448         return true;
449     }
450 
requestSessionCommon(PrintWriter pw, CountDownLatch latch, Runnable command)451     private int requestSessionCommon(PrintWriter pw, CountDownLatch latch,
452             Runnable command) {
453         command.run();
454         return waitForLatch(pw, latch);
455     }
456 
waitForLatch(PrintWriter pw, CountDownLatch latch)457     private int waitForLatch(PrintWriter pw, CountDownLatch latch) {
458         try {
459             final boolean received = latch.await(5, TimeUnit.SECONDS);
460             if (!received) {
461                 pw.println("Timed out after 5 seconds");
462                 return -1;
463             }
464         } catch (InterruptedException e) {
465             pw.println("System call interrupted");
466             Thread.currentThread().interrupt();
467             return -1;
468         }
469         return 0;
470     }
471 
requestReset()472     private int requestReset() {
473         mService.reset();
474         return 0;
475     }
476 
getUserIdFromArgsOrAllUsers()477     private int getUserIdFromArgsOrAllUsers() {
478         if ("--user".equals(getNextArg())) {
479             return UserHandle.parseUserArg(getNextArgRequired());
480         }
481         return UserHandle.USER_ALL;
482     }
483 
getNextIntArgRequired()484     private int getNextIntArgRequired() {
485         return Integer.parseInt(getNextArgRequired());
486     }
487 }
488