1 /*
2  * Copyright (C) 2020 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 package com.android.server.voiceinteraction;
17 
18 import android.os.Bundle;
19 import android.os.RemoteException;
20 import android.os.ShellCommand;
21 import android.util.Slog;
22 
23 import com.android.internal.app.IVoiceInteractionSessionShowCallback;
24 import com.android.server.voiceinteraction.VoiceInteractionManagerService.VoiceInteractionManagerServiceStub;
25 
26 import java.io.PrintWriter;
27 import java.util.concurrent.CountDownLatch;
28 import java.util.concurrent.TimeUnit;
29 import java.util.concurrent.atomic.AtomicInteger;
30 
31 /**
32  * Shell command for {@link VoiceInteractionManagerService}.
33  */
34 final class VoiceInteractionManagerServiceShellCommand extends ShellCommand {
35     private static final String TAG = "VoiceInteractionManager";
36     private static final long TIMEOUT_MS = 5_000;
37 
38     private final VoiceInteractionManagerServiceStub mService;
39 
VoiceInteractionManagerServiceShellCommand(VoiceInteractionManagerServiceStub service)40     VoiceInteractionManagerServiceShellCommand(VoiceInteractionManagerServiceStub service) {
41         mService = service;
42     }
43 
44     @Override
onCommand(String cmd)45     public int onCommand(String cmd) {
46         if (cmd == null) {
47             return handleDefaultCommands(cmd);
48         }
49         PrintWriter pw = getOutPrintWriter();
50         switch (cmd) {
51             case "show":
52                 return requestShow(pw);
53             case "hide":
54                 return requestHide(pw);
55             case "disable":
56                 return requestDisable(pw);
57             case "restart-detection":
58                 return requestRestartDetection(pw);
59             case "set-debug-hotword-logging":
60                 return setDebugHotwordLogging(pw);
61             default:
62                 return handleDefaultCommands(cmd);
63         }
64     }
65 
66     @Override
onHelp()67     public void onHelp() {
68         try (PrintWriter pw = getOutPrintWriter();) {
69             pw.println("VoiceInteraction Service (voiceinteraction) commands:");
70             pw.println("  help");
71             pw.println("    Prints this help text.");
72             pw.println("");
73             pw.println("  show");
74             pw.println("    Shows a session for the active service");
75             pw.println("");
76             pw.println("  hide");
77             pw.println("    Hides the current session");
78             pw.println("");
79             pw.println("  disable [true|false]");
80             pw.println("    Temporarily disable (when true) service");
81             pw.println("");
82             pw.println("  restart-detection");
83             pw.println("    Force a restart of a hotword detection service");
84             pw.println("");
85             pw.println("  set-debug-hotword-logging [true|false]");
86             pw.println("    Temporarily enable or disable debug logging for hotword result.");
87             pw.println("    The debug logging will be reset after one hour from last enable.");
88             pw.println("");
89         }
90     }
91 
requestShow(PrintWriter pw)92     private int requestShow(PrintWriter pw) {
93         Slog.i(TAG, "requestShow()");
94         CountDownLatch latch = new CountDownLatch(1);
95         AtomicInteger result = new AtomicInteger();
96 
97         IVoiceInteractionSessionShowCallback callback =
98                 new IVoiceInteractionSessionShowCallback.Stub() {
99             @Override
100             public void onFailed() throws RemoteException {
101                 Slog.w(TAG, "onFailed()");
102                 pw.println("callback failed");
103                 result.set(1);
104                 latch.countDown();
105             }
106 
107             @Override
108             public void onShown() throws RemoteException {
109                 Slog.d(TAG, "onShown()");
110                 result.set(0);
111                 latch.countDown();
112             }
113         };
114 
115         try {
116             Bundle args = new Bundle();
117             boolean ok = mService.showSessionForActiveService(args, /* sourceFlags= */ 0, callback,
118                     /* activityToken= */ null);
119 
120             if (!ok) {
121                 pw.println("showSessionForActiveService() returned false");
122                 return 1;
123             }
124 
125             if (!latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
126                 pw.printf("Callback not called in %d ms\n", TIMEOUT_MS);
127                 return 1;
128             }
129         } catch (Exception e) {
130             return handleError(pw, "showSessionForActiveService()", e);
131         }
132 
133         return 0;
134     }
135 
requestHide(PrintWriter pw)136     private int requestHide(PrintWriter pw) {
137         Slog.i(TAG, "requestHide()");
138         try {
139             mService.hideCurrentSession();
140         } catch (Exception e) {
141             return handleError(pw, "requestHide()", e);
142         }
143         return 0;
144     }
145 
requestDisable(PrintWriter pw)146     private int requestDisable(PrintWriter pw) {
147         boolean disabled = Boolean.parseBoolean(getNextArgRequired());
148         Slog.i(TAG, "requestDisable(): " + disabled);
149         try {
150             mService.setDisabled(disabled);
151         } catch (Exception e) {
152             return handleError(pw, "requestDisable()", e);
153         }
154         return 0;
155     }
156 
requestRestartDetection(PrintWriter pw)157     private int requestRestartDetection(PrintWriter pw) {
158         Slog.i(TAG, "requestRestartDetection()");
159         try {
160             mService.forceRestartHotwordDetector();
161         } catch (Exception e) {
162             return handleError(pw, "requestRestartDetection()", e);
163         }
164         return 0;
165     }
166 
setDebugHotwordLogging(PrintWriter pw)167     private int setDebugHotwordLogging(PrintWriter pw) {
168         boolean logging = Boolean.parseBoolean(getNextArgRequired());
169         Slog.i(TAG, "setDebugHotwordLogging(): " + logging);
170         try {
171             mService.setDebugHotwordLogging(logging);
172         } catch (Exception e) {
173             return handleError(pw, "setDebugHotwordLogging()", e);
174         }
175         return 0;
176     }
177 
handleError(PrintWriter pw, String message, Exception e)178     private static int handleError(PrintWriter pw, String message, Exception e) {
179         Slog.e(TAG,  "error calling " + message, e);
180         pw.printf("Error calling %s: %s\n", message, e);
181         return 1;
182     }
183 }
184