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