• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.android.systemui.assist;
2 
3 import static com.android.systemui.DejankUtils.whitelistIpcs;
4 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED;
5 
6 import android.annotation.NonNull;
7 import android.annotation.Nullable;
8 import android.app.ActivityOptions;
9 import android.app.SearchManager;
10 import android.content.ActivityNotFoundException;
11 import android.content.ComponentName;
12 import android.content.Context;
13 import android.content.Intent;
14 import android.metrics.LogMaker;
15 import android.os.AsyncTask;
16 import android.os.Bundle;
17 import android.os.Handler;
18 import android.os.RemoteException;
19 import android.os.SystemClock;
20 import android.os.UserHandle;
21 import android.provider.Settings;
22 import android.service.voice.VoiceInteractionSession;
23 import android.util.Log;
24 
25 import com.android.internal.app.AssistUtils;
26 import com.android.internal.app.IVoiceInteractionSessionListener;
27 import com.android.internal.logging.MetricsLogger;
28 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
29 import com.android.keyguard.KeyguardUpdateMonitor;
30 import com.android.systemui.R;
31 import com.android.systemui.assist.ui.DefaultUiController;
32 import com.android.systemui.dagger.SysUISingleton;
33 import com.android.systemui.dagger.qualifiers.Main;
34 import com.android.systemui.model.SysUiState;
35 import com.android.systemui.recents.OverviewProxyService;
36 import com.android.systemui.settings.DisplayTracker;
37 import com.android.systemui.settings.UserTracker;
38 import com.android.systemui.statusbar.CommandQueue;
39 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
40 import com.android.systemui.util.settings.SecureSettings;
41 
42 import javax.inject.Inject;
43 
44 import dagger.Lazy;
45 
46 /**
47  * Class to manage everything related to assist in SystemUI.
48  */
49 @SysUISingleton
50 public class AssistManager {
51 
52     /**
53      * Controls the UI for showing Assistant invocation progress.
54      */
55     public interface UiController {
56         /**
57          * Updates the invocation progress.
58          *
59          * @param type     one of INVOCATION_TYPE_GESTURE, INVOCATION_TYPE_ACTIVE_EDGE,
60          *                 INVOCATION_TYPE_VOICE, INVOCATION_TYPE_QUICK_SEARCH_BAR,
61          *                 INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS
62          * @param progress a float between 0 and 1 inclusive. 0 represents the beginning of the
63          *                 gesture; 1 represents the end.
64          */
onInvocationProgress(int type, float progress)65         void onInvocationProgress(int type, float progress);
66 
67         /**
68          * Called when an invocation gesture completes.
69          *
70          * @param velocity the speed of the invocation gesture, in pixels per millisecond. For
71          *                 drags, this is 0.
72          */
onGestureCompletion(float velocity)73         void onGestureCompletion(float velocity);
74 
75         /**
76          * Hides any SysUI for the assistant, but _does not_ close the assistant itself.
77          */
hide()78         void hide();
79     }
80 
81     private static final String TAG = "AssistManager";
82 
83     // Note that VERBOSE logging may leak PII (e.g. transcription contents).
84     private static final boolean VERBOSE = false;
85 
86     private static final String INVOCATION_TIME_MS_KEY = "invocation_time_ms";
87     private static final String INVOCATION_PHONE_STATE_KEY = "invocation_phone_state";
88     protected static final String ACTION_KEY = "action";
89     protected static final String SET_ASSIST_GESTURE_CONSTRAINED_ACTION =
90             "set_assist_gesture_constrained";
91     protected static final String CONSTRAINED_KEY = "should_constrain";
92 
93     public static final String INVOCATION_TYPE_KEY = "invocation_type";
94     public static final int INVOCATION_TYPE_UNKNOWN =
95             AssistUtils.INVOCATION_TYPE_UNKNOWN;
96     public static final int INVOCATION_TYPE_GESTURE =
97             AssistUtils.INVOCATION_TYPE_GESTURE;
98     public static final int INVOCATION_TYPE_OTHER =
99             AssistUtils.INVOCATION_TYPE_PHYSICAL_GESTURE;
100     public static final int INVOCATION_TYPE_VOICE =
101             AssistUtils.INVOCATION_TYPE_VOICE;
102     public static final int INVOCATION_TYPE_QUICK_SEARCH_BAR =
103             AssistUtils.INVOCATION_TYPE_QUICK_SEARCH_BAR;
104     public static final int INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS =
105             AssistUtils.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS;
106     public static final int INVOCATION_TYPE_POWER_BUTTON_LONG_PRESS =
107             AssistUtils.INVOCATION_TYPE_POWER_BUTTON_LONG_PRESS;
108 
109     public static final int DISMISS_REASON_INVOCATION_CANCELLED = 1;
110     public static final int DISMISS_REASON_TAP = 2;
111     public static final int DISMISS_REASON_BACK = 3;
112     public static final int DISMISS_REASON_TIMEOUT = 4;
113 
114     private static final long TIMEOUT_SERVICE = 2500;
115     private static final long TIMEOUT_ACTIVITY = 1000;
116 
117     protected final Context mContext;
118     private final AssistDisclosure mAssistDisclosure;
119     private final PhoneStateMonitor mPhoneStateMonitor;
120     private final UiController mUiController;
121     protected final Lazy<SysUiState> mSysUiState;
122     protected final AssistLogger mAssistLogger;
123     private final UserTracker mUserTracker;
124     private final DisplayTracker mDisplayTracker;
125     private final SecureSettings mSecureSettings;
126 
127     private final DeviceProvisionedController mDeviceProvisionedController;
128     private final CommandQueue mCommandQueue;
129     protected final AssistUtils mAssistUtils;
130 
131     @Inject
AssistManager( DeviceProvisionedController controller, Context context, AssistUtils assistUtils, CommandQueue commandQueue, PhoneStateMonitor phoneStateMonitor, OverviewProxyService overviewProxyService, Lazy<SysUiState> sysUiState, DefaultUiController defaultUiController, AssistLogger assistLogger, @Main Handler uiHandler, UserTracker userTracker, DisplayTracker displayTracker, SecureSettings secureSettings)132     public AssistManager(
133             DeviceProvisionedController controller,
134             Context context,
135             AssistUtils assistUtils,
136             CommandQueue commandQueue,
137             PhoneStateMonitor phoneStateMonitor,
138             OverviewProxyService overviewProxyService,
139             Lazy<SysUiState> sysUiState,
140             DefaultUiController defaultUiController,
141             AssistLogger assistLogger,
142             @Main Handler uiHandler,
143             UserTracker userTracker,
144             DisplayTracker displayTracker,
145             SecureSettings secureSettings) {
146         mContext = context;
147         mDeviceProvisionedController = controller;
148         mCommandQueue = commandQueue;
149         mAssistUtils = assistUtils;
150         mAssistDisclosure = new AssistDisclosure(context, uiHandler);
151         mPhoneStateMonitor = phoneStateMonitor;
152         mAssistLogger = assistLogger;
153         mUserTracker = userTracker;
154         mDisplayTracker = displayTracker;
155         mSecureSettings = secureSettings;
156 
157         registerVoiceInteractionSessionListener();
158 
159         mUiController = defaultUiController;
160 
161         mSysUiState = sysUiState;
162 
163         overviewProxyService.addCallback(new OverviewProxyService.OverviewProxyListener() {
164             @Override
165             public void onAssistantProgress(float progress) {
166                 // Progress goes from 0 to 1 to indicate how close the assist gesture is to
167                 // completion.
168                 onInvocationProgress(INVOCATION_TYPE_GESTURE, progress);
169             }
170 
171             @Override
172             public void onAssistantGestureCompletion(float velocity) {
173                 onGestureCompletion(velocity);
174             }
175         });
176     }
177 
registerVoiceInteractionSessionListener()178     protected void registerVoiceInteractionSessionListener() {
179         mAssistUtils.registerVoiceInteractionSessionListener(
180                 new IVoiceInteractionSessionListener.Stub() {
181                     @Override
182                     public void onVoiceSessionShown() throws RemoteException {
183                         if (VERBOSE) {
184                             Log.v(TAG, "Voice open");
185                         }
186                         mAssistLogger.reportAssistantSessionEvent(
187                                 AssistantSessionEvent.ASSISTANT_SESSION_UPDATE);
188                     }
189 
190                     @Override
191                     public void onVoiceSessionHidden() throws RemoteException {
192                         if (VERBOSE) {
193                             Log.v(TAG, "Voice closed");
194                         }
195                         mAssistLogger.reportAssistantSessionEvent(
196                                 AssistantSessionEvent.ASSISTANT_SESSION_CLOSE);
197                     }
198 
199                     @Override
200                     public void onVoiceSessionWindowVisibilityChanged(boolean visible)
201                             throws RemoteException {
202                         if (VERBOSE) {
203                             Log.v(TAG, "Window visibility changed: " + visible);
204                         }
205                     }
206 
207                     @Override
208                     public void onSetUiHints(Bundle hints) {
209                         if (VERBOSE) {
210                             Log.v(TAG, "UI hints received");
211                         }
212 
213                         String action = hints.getString(ACTION_KEY);
214                         if (SET_ASSIST_GESTURE_CONSTRAINED_ACTION.equals(action)) {
215                             mSysUiState.get()
216                                     .setFlag(
217                                             SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED,
218                                             hints.getBoolean(CONSTRAINED_KEY, false))
219                                     .commitUpdate(mDisplayTracker.getDefaultDisplayId());
220                         }
221                     }
222                 });
223     }
224 
startAssist(Bundle args)225     public void startAssist(Bundle args) {
226         final ComponentName assistComponent = getAssistInfo();
227         if (assistComponent == null) {
228             return;
229         }
230 
231         final boolean isService = assistComponent.equals(getVoiceInteractorComponentName());
232 
233         if (args == null) {
234             args = new Bundle();
235         }
236         int legacyInvocationType = args.getInt(INVOCATION_TYPE_KEY, 0);
237         int legacyDeviceState = mPhoneStateMonitor.getPhoneState();
238         args.putInt(INVOCATION_PHONE_STATE_KEY, legacyDeviceState);
239         args.putLong(INVOCATION_TIME_MS_KEY, SystemClock.elapsedRealtime());
240         mAssistLogger.reportAssistantInvocationEventFromLegacy(
241                 legacyInvocationType,
242                 /* isInvocationComplete = */ true,
243                 assistComponent,
244                 legacyDeviceState);
245         logStartAssistLegacy(legacyInvocationType, legacyDeviceState);
246         startAssistInternal(args, assistComponent, isService);
247     }
248 
249     /** Called when the user is performing an assistant invocation action (e.g. Active Edge) */
onInvocationProgress(int type, float progress)250     public void onInvocationProgress(int type, float progress) {
251         mUiController.onInvocationProgress(type, progress);
252     }
253 
254     /**
255      * Called when the user has invoked the assistant with the incoming velocity, in pixels per
256      * millisecond. For invocations without a velocity (e.g. slow drag), the velocity is set to
257      * zero.
258      */
onGestureCompletion(float velocity)259     public void onGestureCompletion(float velocity) {
260         mUiController.onGestureCompletion(velocity);
261     }
262 
hideAssist()263     public void hideAssist() {
264         mAssistUtils.hideCurrentSession();
265     }
266 
startAssistInternal(Bundle args, @NonNull ComponentName assistComponent, boolean isService)267     private void startAssistInternal(Bundle args, @NonNull ComponentName assistComponent,
268             boolean isService) {
269         if (isService) {
270             startVoiceInteractor(args);
271         } else {
272             startAssistActivity(args, assistComponent);
273         }
274     }
275 
startAssistActivity(Bundle args, @NonNull ComponentName assistComponent)276     private void startAssistActivity(Bundle args, @NonNull ComponentName assistComponent) {
277         if (!mDeviceProvisionedController.isDeviceProvisioned()) {
278             return;
279         }
280 
281         // Close Recent Apps if needed
282         mCommandQueue.animateCollapsePanels(
283                 CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL | CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
284                 false /* force */);
285 
286         boolean structureEnabled = mSecureSettings.getIntForUser(
287                 Settings.Secure.ASSIST_STRUCTURE_ENABLED, 1, UserHandle.USER_CURRENT) != 0;
288 
289         final SearchManager searchManager =
290                 (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
291         if (searchManager == null) {
292             return;
293         }
294         final Intent intent = searchManager.getAssistIntent(structureEnabled);
295         if (intent == null) {
296             return;
297         }
298         intent.setComponent(assistComponent);
299         intent.putExtras(args);
300 
301         if (structureEnabled && AssistUtils.isDisclosureEnabled(mContext)) {
302             showDisclosure();
303         }
304 
305         try {
306             final ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext,
307                     R.anim.search_launch_enter, R.anim.search_launch_exit);
308             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
309             AsyncTask.execute(new Runnable() {
310                 @Override
311                 public void run() {
312                     mContext.startActivityAsUser(intent, opts.toBundle(),
313                             mUserTracker.getUserHandle());
314                 }
315             });
316         } catch (ActivityNotFoundException e) {
317             Log.w(TAG, "Activity not found for " + intent.getAction());
318         }
319     }
320 
startVoiceInteractor(Bundle args)321     private void startVoiceInteractor(Bundle args) {
322         mAssistUtils.showSessionForActiveService(args,
323                 VoiceInteractionSession.SHOW_SOURCE_ASSIST_GESTURE, null, null);
324     }
325 
launchVoiceAssistFromKeyguard()326     public void launchVoiceAssistFromKeyguard() {
327         mAssistUtils.launchVoiceAssistFromKeyguard();
328     }
329 
canVoiceAssistBeLaunchedFromKeyguard()330     public boolean canVoiceAssistBeLaunchedFromKeyguard() {
331         // TODO(b/140051519)
332         return whitelistIpcs(() -> mAssistUtils.activeServiceSupportsLaunchFromKeyguard());
333     }
334 
getVoiceInteractorComponentName()335     public ComponentName getVoiceInteractorComponentName() {
336         return mAssistUtils.getActiveServiceComponentName();
337     }
338 
isVoiceSessionRunning()339     private boolean isVoiceSessionRunning() {
340         return mAssistUtils.isSessionRunning();
341     }
342 
343     @Nullable
getAssistInfoForUser(int userId)344     public ComponentName getAssistInfoForUser(int userId) {
345         return mAssistUtils.getAssistComponentForUser(userId);
346     }
347 
348     @Nullable
getAssistInfo()349     private ComponentName getAssistInfo() {
350         return getAssistInfoForUser(KeyguardUpdateMonitor.getCurrentUser());
351     }
352 
showDisclosure()353     public void showDisclosure() {
354         mAssistDisclosure.postShow();
355     }
356 
onLockscreenShown()357     public void onLockscreenShown() {
358         AsyncTask.execute(new Runnable() {
359             @Override
360             public void run() {
361                 mAssistUtils.onLockscreenShown();
362             }
363         });
364     }
365 
366     /** Returns the logging flags for the given Assistant invocation type. */
toLoggingSubType(int invocationType)367     public int toLoggingSubType(int invocationType) {
368         return toLoggingSubType(invocationType, mPhoneStateMonitor.getPhoneState());
369     }
370 
logStartAssistLegacy(int invocationType, int phoneState)371     protected void logStartAssistLegacy(int invocationType, int phoneState) {
372         MetricsLogger.action(
373                 new LogMaker(MetricsEvent.ASSISTANT)
374                         .setType(MetricsEvent.TYPE_OPEN)
375                         .setSubtype(toLoggingSubType(invocationType, phoneState)));
376     }
377 
toLoggingSubType(int invocationType, int phoneState)378     protected final int toLoggingSubType(int invocationType, int phoneState) {
379         // Note that this logic will break if the number of Assistant invocation types exceeds 7.
380         // There are currently 5 invocation types, but we will be migrating to the new logging
381         // framework in the next update.
382         int subType = 0;
383         subType |= invocationType << 1;
384         subType |= phoneState << 4;
385         return subType;
386     }
387 }
388