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