1 /*
2  * Copyright (C) 2015 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.voiceinteraction;
18 
19 import static android.app.AppOpsManager.OP_ASSIST_SCREENSHOT;
20 import static android.app.AppOpsManager.OP_ASSIST_STRUCTURE;
21 import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
22 import static android.content.Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
23 import static android.view.Display.DEFAULT_DISPLAY;
24 import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
25 
26 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_ACTIVITY_ID;
27 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT;
28 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA;
29 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
30 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_TASK_ID;
31 
32 import android.annotation.NonNull;
33 import android.annotation.Nullable;
34 import android.app.ActivityManager;
35 import android.app.ActivityTaskManager;
36 import android.app.AppOpsManager;
37 import android.app.IActivityManager;
38 import android.app.UriGrantsManager;
39 import android.app.assist.AssistContent;
40 import android.app.assist.AssistStructure;
41 import android.content.ClipData;
42 import android.content.ComponentName;
43 import android.content.ContentProvider;
44 import android.content.Context;
45 import android.content.Intent;
46 import android.content.ServiceConnection;
47 import android.graphics.Bitmap;
48 import android.hardware.power.Boost;
49 import android.net.Uri;
50 import android.os.Binder;
51 import android.os.Bundle;
52 import android.os.Handler;
53 import android.os.IBinder;
54 import android.os.PowerManager;
55 import android.os.PowerManagerInternal;
56 import android.os.RemoteException;
57 import android.os.ServiceManager;
58 import android.os.UserHandle;
59 import android.provider.Settings;
60 import android.service.voice.IVoiceInteractionSession;
61 import android.service.voice.IVoiceInteractionSessionService;
62 import android.service.voice.VisibleActivityInfo;
63 import android.service.voice.VoiceInteractionService;
64 import android.service.voice.VoiceInteractionSession;
65 import android.util.ArrayMap;
66 import android.util.Slog;
67 import android.view.IWindowManager;
68 
69 import com.android.internal.app.AssistUtils;
70 import com.android.internal.app.IVoiceInteractionSessionShowCallback;
71 import com.android.internal.app.IVoiceInteractor;
72 import com.android.server.FgThread;
73 import com.android.server.LocalServices;
74 import com.android.server.am.AssistDataRequester;
75 import com.android.server.am.AssistDataRequester.AssistDataRequesterCallbacks;
76 import com.android.server.power.LowPowerStandbyControllerInternal;
77 import com.android.server.statusbar.StatusBarManagerInternal;
78 import com.android.server.uri.UriGrantsManagerInternal;
79 import com.android.server.wm.ActivityAssistInfo;
80 import com.android.server.wm.ActivityTaskManagerInternal;
81 
82 import java.io.PrintWriter;
83 import java.time.Instant;
84 import java.util.ArrayList;
85 import java.util.List;
86 import java.util.concurrent.Executors;
87 import java.util.concurrent.ScheduledExecutorService;
88 
89 final class VoiceInteractionSessionConnection implements ServiceConnection,
90         AssistDataRequesterCallbacks {
91 
92     static final String TAG = "VoiceInteractionServiceManager";
93     static final boolean DEBUG = false;
94     static final int POWER_BOOST_TIMEOUT_MS = Integer.parseInt(
95             System.getProperty("vendor.powerhal.interaction.max", "200"));
96     static final int BOOST_TIMEOUT_MS = 300;
97     /**
98      * The maximum time an app can stay on the Low Power Standby allowlist when
99      * the session is shown. There to safeguard against apps that don't call hide.
100      */
101     private static final int LOW_POWER_STANDBY_ALLOWLIST_TIMEOUT_MS = 120_000;
102     // TODO: To avoid ap doesn't call hide, only 10 secs for now, need a better way to manage it
103     //  in the future.
104     static final int MAX_POWER_BOOST_TIMEOUT = 10_000;
105 
106     final IBinder mToken = new Binder();
107     final Object mLock;
108     final ComponentName mSessionComponentName;
109     final Intent mBindIntent;
110     final int mUser;
111     final Context mContext;
112     final Callback mCallback;
113     final int mCallingUid;
114     final Handler mHandler;
115     final IActivityManager mAm;
116     final UriGrantsManagerInternal mUgmInternal;
117     final IWindowManager mIWindowManager;
118     final AppOpsManager mAppOps;
119     final IBinder mPermissionOwner;
120     boolean mShown;
121     Bundle mShowArgs;
122     int mShowFlags;
123     boolean mBound;
124     boolean mFullyBound;
125     boolean mCanceled;
126     IVoiceInteractionSessionService mService;
127     IVoiceInteractionSession mSession;
128     IVoiceInteractor mInteractor;
129     ArrayList<IVoiceInteractionSessionShowCallback> mPendingShowCallbacks = new ArrayList<>();
130     private List<ActivityAssistInfo> mPendingHandleAssistWithoutData = new ArrayList<>();
131     AssistDataRequester mAssistDataRequester;
132     private boolean mListeningVisibleActivity;
133     private final ScheduledExecutorService mScheduledExecutorService =
134             Executors.newSingleThreadScheduledExecutor();
135     // Records the visible activity information the system has already called onVisible, without
136     // confirming the result of callback. When activity visible state is changed, we use this to
137     // determine to call onVisible or onInvisible to assistant application.
138     private final ArrayMap<IBinder, VisibleActivityInfo> mVisibleActivityInfoForToken =
139             new ArrayMap<>();
140     private final PowerManagerInternal mPowerManagerInternal;
141     private final LowPowerStandbyControllerInternal mLowPowerStandbyControllerInternal;
142     private final Runnable mRemoveFromLowPowerStandbyAllowlistRunnable =
143             this::removeFromLowPowerStandbyAllowlist;
144     private boolean mLowPowerStandbyAllowlisted;
145     private PowerBoostSetter mSetPowerBoostRunnable;
146     private final Handler mFgHandler;
147 
148     class PowerBoostSetter implements Runnable {
149 
150         private boolean mCanceled;
151         private final Instant mExpiryTime;
152 
PowerBoostSetter(Instant expiryTime)153         PowerBoostSetter(Instant expiryTime) {
154             mExpiryTime = expiryTime;
155         }
156 
157         @Override
run()158         public void run() {
159             synchronized (mLock) {
160                 if (mCanceled) {
161                     return;
162                 }
163                 // To avoid voice interaction service does not call hide to cancel setting
164                 // power boost. We will cancel set boost when reaching the max timeout.
165                 if (Instant.now().isBefore(mExpiryTime)) {
166                     mPowerManagerInternal.setPowerBoost(Boost.INTERACTION, BOOST_TIMEOUT_MS);
167                     if (mSetPowerBoostRunnable != null) {
168                         mFgHandler.postDelayed(mSetPowerBoostRunnable, POWER_BOOST_TIMEOUT_MS);
169                     }
170                 } else {
171                     Slog.w(TAG, "Reset power boost INTERACTION because reaching max timeout.");
172                     mPowerManagerInternal.setPowerBoost(Boost.INTERACTION, /* durationMs */ -1);
173                 }
174             }
175         }
176 
cancel()177         void cancel() {
178             synchronized (mLock) {
179                 mCanceled =  true;
180             }
181         }
182     }
183 
184     IVoiceInteractionSessionShowCallback mShowCallback =
185             new IVoiceInteractionSessionShowCallback.Stub() {
186         @Override
187         public void onFailed() throws RemoteException {
188             synchronized (mLock) {
189                 notifyPendingShowCallbacksFailedLocked();
190             }
191         }
192 
193         @Override
194         public void onShown() throws RemoteException {
195             synchronized (mLock) {
196                 // TODO: Figure out whether this is good enough or whether we need to hook into
197                 // Window manager to actually wait for the window to be drawn.
198                 notifyPendingShowCallbacksShownLocked();
199             }
200         }
201     };
202 
203     public interface Callback {
sessionConnectionGone(VoiceInteractionSessionConnection connection)204         public void sessionConnectionGone(VoiceInteractionSessionConnection connection);
onSessionShown(VoiceInteractionSessionConnection connection)205         public void onSessionShown(VoiceInteractionSessionConnection connection);
onSessionHidden(VoiceInteractionSessionConnection connection)206         public void onSessionHidden(VoiceInteractionSessionConnection connection);
207     }
208 
209     final ServiceConnection mFullConnection = new ServiceConnection() {
210         @Override
211         public void onServiceConnected(ComponentName name, IBinder service) {
212         }
213         @Override
214         public void onServiceDisconnected(ComponentName name) {
215         }
216     };
217 
VoiceInteractionSessionConnection(Object lock, ComponentName component, int user, Context context, Callback callback, int callingUid, Handler handler)218     public VoiceInteractionSessionConnection(Object lock, ComponentName component, int user,
219             Context context, Callback callback, int callingUid, Handler handler) {
220         mLock = lock;
221         mSessionComponentName = component;
222         mUser = user;
223         mContext = context;
224         mCallback = callback;
225         mCallingUid = callingUid;
226         mHandler = handler;
227         mAm = ActivityManager.getService();
228         mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
229         mIWindowManager = IWindowManager.Stub.asInterface(
230                 ServiceManager.getService(Context.WINDOW_SERVICE));
231         mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
232         mLowPowerStandbyControllerInternal = LocalServices.getService(
233                 LowPowerStandbyControllerInternal.class);
234         mAppOps = context.getSystemService(AppOpsManager.class);
235         mFgHandler = FgThread.getHandler();
236         mAssistDataRequester = new AssistDataRequester(mContext, mIWindowManager,
237                 (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE),
238                 this, mLock, OP_ASSIST_STRUCTURE, OP_ASSIST_SCREENSHOT);
239         final IBinder permOwner = mUgmInternal.newUriPermissionOwner("voicesession:"
240                     + component.flattenToShortString());
241         mPermissionOwner = permOwner;
242         mBindIntent = new Intent(VoiceInteractionService.SERVICE_INTERFACE);
243         mBindIntent.setComponent(mSessionComponentName);
244         mBound = mContext.bindServiceAsUser(mBindIntent, this,
245                 Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY
246                         | Context.BIND_ALLOW_OOM_MANAGEMENT
247                         | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS, new UserHandle(mUser));
248         if (mBound) {
249             try {
250                 mIWindowManager.addWindowToken(mToken, TYPE_VOICE_INTERACTION, DEFAULT_DISPLAY,
251                         null /* options */);
252             } catch (RemoteException e) {
253                 Slog.w(TAG, "Failed adding window token", e);
254             }
255         } else {
256             Slog.w(TAG, "Failed binding to voice interaction session service "
257                     + mSessionComponentName);
258         }
259     }
260 
getUserDisabledShowContextLocked()261     public int getUserDisabledShowContextLocked() {
262         int flags = 0;
263         if (Settings.Secure.getIntForUser(mContext.getContentResolver(),
264                 Settings.Secure.ASSIST_STRUCTURE_ENABLED, 1, mUser) == 0) {
265             flags |= VoiceInteractionSession.SHOW_WITH_ASSIST;
266         }
267         if (Settings.Secure.getIntForUser(mContext.getContentResolver(),
268                 Settings.Secure.ASSIST_SCREENSHOT_ENABLED, 1, mUser) == 0) {
269             flags |= VoiceInteractionSession.SHOW_WITH_SCREENSHOT;
270         }
271         return flags;
272     }
273 
showLocked(@onNull Bundle args, int flags, @Nullable String attributionTag, int disabledContext, @Nullable IVoiceInteractionSessionShowCallback showCallback, @NonNull List<ActivityAssistInfo> topActivities)274     public boolean showLocked(@NonNull Bundle args, int flags, @Nullable String attributionTag,
275             int disabledContext, @Nullable IVoiceInteractionSessionShowCallback showCallback,
276             @NonNull List<ActivityAssistInfo> topActivities) {
277         if (mBound) {
278             if (!mFullyBound) {
279                 mFullyBound = mContext.bindServiceAsUser(mBindIntent, mFullConnection,
280                         Context.BIND_AUTO_CREATE | Context.BIND_TREAT_LIKE_ACTIVITY
281                                 | Context.BIND_SCHEDULE_LIKE_TOP_APP
282                                 | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS,
283                         new UserHandle(mUser));
284             }
285 
286             mShown = true;
287             mShowArgs = args;
288             mShowFlags = flags;
289 
290             disabledContext |= getUserDisabledShowContextLocked();
291 
292             boolean fetchData = (flags & VoiceInteractionSession.SHOW_WITH_ASSIST) != 0;
293             boolean fetchScreenshot = (flags & VoiceInteractionSession.SHOW_WITH_SCREENSHOT) != 0;
294             boolean assistDataRequestNeeded = fetchData || fetchScreenshot;
295 
296             if (assistDataRequestNeeded) {
297                 int topActivitiesCount = topActivities.size();
298                 final ArrayList<IBinder> topActivitiesToken = new ArrayList<>(topActivitiesCount);
299                 for (int i = 0; i < topActivitiesCount; i++) {
300                     topActivitiesToken.add(topActivities.get(i).getActivityToken());
301                 }
302 
303                 mAssistDataRequester.requestAssistData(topActivitiesToken,
304                         fetchData,
305                         fetchScreenshot,
306                         (disabledContext & VoiceInteractionSession.SHOW_WITH_ASSIST) == 0,
307                         (disabledContext & VoiceInteractionSession.SHOW_WITH_SCREENSHOT) == 0,
308                         mCallingUid,
309                         mSessionComponentName.getPackageName(),
310                         attributionTag);
311 
312                 boolean needDisclosure = mAssistDataRequester.getPendingDataCount() > 0
313                         || mAssistDataRequester.getPendingScreenshotCount() > 0;
314                 if (needDisclosure && AssistUtils.shouldDisclose(mContext, mSessionComponentName)) {
315                     mHandler.post(mShowAssistDisclosureRunnable);
316                 }
317             }
318             if (mSession != null) {
319                 try {
320                     mSession.show(mShowArgs, mShowFlags, showCallback);
321                     mShowArgs = null;
322                     mShowFlags = 0;
323                 } catch (RemoteException e) {
324                 }
325                 if (assistDataRequestNeeded) {
326                     mAssistDataRequester.processPendingAssistData();
327                 } else {
328                     doHandleAssistWithoutData(topActivities);
329                 }
330             } else {
331                 if (showCallback != null) {
332                     mPendingShowCallbacks.add(showCallback);
333                 }
334                 if (!assistDataRequestNeeded) {
335                     // If no data are required we are not passing trough mAssistDataRequester. As
336                     // a consequence, when a new session is delivered it is needed to process those
337                     // requests manually.
338                     mPendingHandleAssistWithoutData = topActivities;
339                 }
340             }
341             // remove if already existing one.
342             if (mSetPowerBoostRunnable != null) {
343                 mSetPowerBoostRunnable.cancel();
344             }
345             mSetPowerBoostRunnable = new PowerBoostSetter(
346                     Instant.now().plusMillis(MAX_POWER_BOOST_TIMEOUT));
347             mFgHandler.post(mSetPowerBoostRunnable);
348 
349             if (mLowPowerStandbyControllerInternal != null) {
350                 mLowPowerStandbyControllerInternal.addToAllowlist(mCallingUid,
351                         PowerManager.LOW_POWER_STANDBY_ALLOWED_REASON_VOICE_INTERACTION);
352                 mLowPowerStandbyAllowlisted = true;
353                 mFgHandler.removeCallbacks(mRemoveFromLowPowerStandbyAllowlistRunnable);
354                 mFgHandler.postDelayed(mRemoveFromLowPowerStandbyAllowlistRunnable,
355                         LOW_POWER_STANDBY_ALLOWLIST_TIMEOUT_MS);
356             }
357 
358             mCallback.onSessionShown(this);
359             return true;
360         }
361         if (showCallback != null) {
362             try {
363                 showCallback.onFailed();
364             } catch (RemoteException e) {
365             }
366         }
367         return false;
368     }
369 
doHandleAssistWithoutData(List<ActivityAssistInfo> topActivities)370     private void doHandleAssistWithoutData(List<ActivityAssistInfo> topActivities) {
371         final int activityCount = topActivities.size();
372         for (int i = 0; i < activityCount; i++) {
373             final ActivityAssistInfo topActivity = topActivities.get(i);
374             final IBinder assistToken = topActivity.getAssistToken();
375             final int taskId = topActivity.getTaskId();
376             final int activityIndex = i;
377             try {
378                 mSession.handleAssist(
379                         taskId,
380                         assistToken,
381                         /* assistData = */ null,
382                         /* assistStructure = */ null,
383                         /* assistContent = */ null,
384                         activityIndex,
385                         activityCount);
386             } catch (RemoteException e) {
387                 // Ignore
388             }
389         }
390     }
391 
392     @Override
canHandleReceivedAssistDataLocked()393     public boolean canHandleReceivedAssistDataLocked() {
394         return mSession != null;
395     }
396 
397     @Override
onAssistDataReceivedLocked(Bundle data, int activityIndex, int activityCount)398     public void onAssistDataReceivedLocked(Bundle data, int activityIndex, int activityCount) {
399         // Return early if we have no session
400         if (mSession == null) {
401             return;
402         }
403 
404         if (data == null) {
405             try {
406                 mSession.handleAssist(-1, null, null, null, null, 0, 0);
407             } catch (RemoteException e) {
408                 // Ignore
409             }
410         } else {
411             final int taskId = data.getInt(ASSIST_TASK_ID);
412             final IBinder activityId = data.getBinder(ASSIST_ACTIVITY_ID);
413             final Bundle assistData = data.getBundle(ASSIST_KEY_DATA);
414             final AssistStructure structure = data.getParcelable(ASSIST_KEY_STRUCTURE, android.app.assist.AssistStructure.class);
415             final AssistContent content = data.getParcelable(ASSIST_KEY_CONTENT, android.app.assist.AssistContent.class);
416             int uid = -1;
417             if (assistData != null) {
418                 uid = assistData.getInt(Intent.EXTRA_ASSIST_UID, -1);
419             }
420             if (uid >= 0 && content != null) {
421                 Intent intent = content.getIntent();
422                 if (intent != null) {
423                     ClipData clipData = intent.getClipData();
424                     if (clipData != null && Intent.isAccessUriMode(intent.getFlags())) {
425                         grantClipDataPermissions(clipData, intent.getFlags(), uid,
426                                 mCallingUid, mSessionComponentName.getPackageName());
427                     }
428                 }
429                 ClipData clipData = content.getClipData();
430                 if (clipData != null) {
431                     grantClipDataPermissions(clipData, FLAG_GRANT_READ_URI_PERMISSION,
432                             uid, mCallingUid, mSessionComponentName.getPackageName());
433                 }
434             }
435             try {
436                 mSession.handleAssist(taskId, activityId, assistData, structure,
437                         content, activityIndex, activityCount);
438             } catch (RemoteException e) {
439                 // Ignore
440             }
441         }
442     }
443 
444     @Override
onAssistScreenshotReceivedLocked(Bitmap screenshot)445     public void onAssistScreenshotReceivedLocked(Bitmap screenshot) {
446         // Return early if we have no session
447         if (mSession == null) {
448             return;
449         }
450 
451         try {
452             mSession.handleScreenshot(screenshot);
453         } catch (RemoteException e) {
454             // Ignore
455         }
456     }
457 
grantUriPermission(Uri uri, int mode, int srcUid, int destUid, String destPkg)458     void grantUriPermission(Uri uri, int mode, int srcUid, int destUid, String destPkg) {
459         if (!"content".equals(uri.getScheme())) {
460             return;
461         }
462         final long ident = Binder.clearCallingIdentity();
463         try {
464             // This will throw SecurityException for us.
465             mUgmInternal.checkGrantUriPermission(srcUid, null,
466                     ContentProvider.getUriWithoutUserId(uri), mode,
467                     ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(srcUid)));
468             // No security exception, do the grant.
469             int sourceUserId = ContentProvider.getUserIdFromUri(uri, mUser);
470             uri = ContentProvider.getUriWithoutUserId(uri);
471             UriGrantsManager.getService().grantUriPermissionFromOwner(mPermissionOwner, srcUid,
472                     destPkg, uri, FLAG_GRANT_READ_URI_PERMISSION, sourceUserId, mUser);
473         } catch (RemoteException e) {
474         } catch (SecurityException e) {
475             Slog.w(TAG, "Can't propagate permission", e);
476         } finally {
477             Binder.restoreCallingIdentity(ident);
478         }
479 
480     }
481 
grantClipDataItemPermission(ClipData.Item item, int mode, int srcUid, int destUid, String destPkg)482     void grantClipDataItemPermission(ClipData.Item item, int mode, int srcUid, int destUid,
483             String destPkg) {
484         if (item.getUri() != null) {
485             grantUriPermission(item.getUri(), mode, srcUid, destUid, destPkg);
486         }
487         Intent intent = item.getIntent();
488         if (intent != null && intent.getData() != null) {
489             grantUriPermission(intent.getData(), mode, srcUid, destUid, destPkg);
490         }
491     }
492 
grantClipDataPermissions(ClipData data, int mode, int srcUid, int destUid, String destPkg)493     void grantClipDataPermissions(ClipData data, int mode, int srcUid, int destUid,
494             String destPkg) {
495         final int N = data.getItemCount();
496         for (int i=0; i<N; i++) {
497             grantClipDataItemPermission(data.getItemAt(i), mode, srcUid, destUid, destPkg);
498         }
499     }
500 
hideLocked()501     public boolean hideLocked() {
502         if (mBound) {
503             if (mShown) {
504                 mShown = false;
505                 mShowArgs = null;
506                 mShowFlags = 0;
507                 mAssistDataRequester.cancel();
508                 mPendingShowCallbacks.clear();
509                 if (mSession != null) {
510                     try {
511                         mSession.hide();
512                     } catch (RemoteException e) {
513                     }
514                 }
515                 mUgmInternal.revokeUriPermissionFromOwner(mPermissionOwner, null,
516                         FLAG_GRANT_READ_URI_PERMISSION | FLAG_GRANT_WRITE_URI_PERMISSION, mUser);
517                 if (mSession != null) {
518                     try {
519                         ActivityTaskManager.getService().finishVoiceTask(mSession);
520                     } catch (RemoteException e) {
521                     }
522                 }
523                 if (mSetPowerBoostRunnable != null) {
524                     mSetPowerBoostRunnable.cancel();
525                     mSetPowerBoostRunnable = null;
526                 }
527                 // A negative value indicates canceling previous boost.
528                 mPowerManagerInternal.setPowerBoost(Boost.INTERACTION, /* durationMs */ -1);
529                 if (mLowPowerStandbyControllerInternal != null) {
530                     removeFromLowPowerStandbyAllowlist();
531                 }
532                 mCallback.onSessionHidden(this);
533             }
534             if (mFullyBound) {
535                 mContext.unbindService(mFullConnection);
536                 mFullyBound = false;
537             }
538             return true;
539         }
540         return false;
541     }
542 
cancelLocked(boolean finishTask)543     public void cancelLocked(boolean finishTask) {
544         mListeningVisibleActivity = false;
545         mVisibleActivityInfoForToken.clear();
546         hideLocked();
547         mCanceled = true;
548         if (mBound) {
549             if (mSession != null) {
550                 try {
551                     mSession.destroy();
552                 } catch (RemoteException e) {
553                     Slog.w(TAG, "Voice interation session already dead");
554                 }
555             }
556             if (finishTask && mSession != null) {
557                 try {
558                     ActivityTaskManager.getService().finishVoiceTask(mSession);
559                 } catch (RemoteException e) {
560                 }
561             }
562             mContext.unbindService(this);
563             try {
564                 mIWindowManager.removeWindowToken(mToken, DEFAULT_DISPLAY);
565             } catch (RemoteException e) {
566                 Slog.w(TAG, "Failed removing window token", e);
567             }
568             mBound = false;
569             mService = null;
570             mSession = null;
571             mInteractor = null;
572         }
573         if (mFullyBound) {
574             mContext.unbindService(mFullConnection);
575             mFullyBound = false;
576         }
577     }
578 
deliverNewSessionLocked(IVoiceInteractionSession session, IVoiceInteractor interactor)579     public boolean deliverNewSessionLocked(IVoiceInteractionSession session,
580             IVoiceInteractor interactor) {
581         mSession = session;
582         mInteractor = interactor;
583         if (mShown) {
584             try {
585                 session.show(mShowArgs, mShowFlags, mShowCallback);
586                 mShowArgs = null;
587                 mShowFlags = 0;
588             } catch (RemoteException e) {
589             }
590             mAssistDataRequester.processPendingAssistData();
591             if (!mPendingHandleAssistWithoutData.isEmpty()) {
592                 doHandleAssistWithoutData(mPendingHandleAssistWithoutData);
593                 mPendingHandleAssistWithoutData.clear();
594             }
595         }
596         return true;
597     }
598 
notifyPendingShowCallbacksShownLocked()599     private void notifyPendingShowCallbacksShownLocked() {
600         for (int i = 0; i < mPendingShowCallbacks.size(); i++) {
601             try {
602                 mPendingShowCallbacks.get(i).onShown();
603             } catch (RemoteException e) {
604             }
605         }
606         mPendingShowCallbacks.clear();
607     }
608 
notifyPendingShowCallbacksFailedLocked()609     private void notifyPendingShowCallbacksFailedLocked() {
610         for (int i = 0; i < mPendingShowCallbacks.size(); i++) {
611             try {
612                 mPendingShowCallbacks.get(i).onFailed();
613             } catch (RemoteException e) {
614             }
615         }
616         mPendingShowCallbacks.clear();
617     }
618 
startListeningVisibleActivityChangedLocked()619     void startListeningVisibleActivityChangedLocked() {
620         if (DEBUG) {
621             Slog.d(TAG, "startListeningVisibleActivityChangedLocked");
622         }
623 
624         if (!mShown || mCanceled || mSession == null) {
625             return;
626         }
627 
628         mListeningVisibleActivity = true;
629         mVisibleActivityInfoForToken.clear();
630 
631         // It should only need to report which activities are visible
632         final ArrayMap<IBinder, VisibleActivityInfo> newVisibleActivityInfos =
633                 getTopVisibleActivityInfosLocked();
634 
635         if (newVisibleActivityInfos == null || newVisibleActivityInfos.isEmpty()) {
636             return;
637         }
638         notifyVisibleActivitiesChangedLocked(newVisibleActivityInfos,
639                 VisibleActivityInfo.TYPE_ACTIVITY_ADDED);
640         mVisibleActivityInfoForToken.putAll(newVisibleActivityInfos);
641     }
642 
stopListeningVisibleActivityChangedLocked()643     void stopListeningVisibleActivityChangedLocked() {
644         if (DEBUG) {
645             Slog.d(TAG, "stopListeningVisibleActivityChangedLocked");
646         }
647         mListeningVisibleActivity = false;
648         mVisibleActivityInfoForToken.clear();
649     }
650 
notifyActivityEventChangedLocked(@onNull IBinder activityToken, int type)651     void notifyActivityEventChangedLocked(@NonNull IBinder activityToken, int type) {
652         if (DEBUG) {
653             Slog.d(TAG, "notifyActivityEventChangedLocked activityToken=" + activityToken
654                     + ", type=" + type);
655         }
656         if (!mListeningVisibleActivity) {
657             if (DEBUG) {
658                 Slog.d(TAG, "not enable listening visible activity");
659             }
660             return;
661         }
662         mScheduledExecutorService.execute(() -> {
663             synchronized (mLock) {
664                 handleVisibleActivitiesLocked(activityToken, type);
665             }
666         });
667     }
668 
getTopVisibleActivityInfosLocked()669     private ArrayMap<IBinder, VisibleActivityInfo> getTopVisibleActivityInfosLocked() {
670         if (DEBUG) {
671             Slog.d(TAG, "getTopVisibleActivityInfosLocked");
672         }
673         List<ActivityAssistInfo> allVisibleActivities =
674                 LocalServices.getService(ActivityTaskManagerInternal.class)
675                         .getTopVisibleActivities();
676         if (DEBUG) {
677             Slog.d(TAG, "getTopVisibleActivityInfosLocked: allVisibleActivities="
678                     + allVisibleActivities);
679         }
680         if (allVisibleActivities.isEmpty()) {
681             Slog.w(TAG, "no visible activity");
682             return null;
683         }
684         final int count = allVisibleActivities.size();
685         final ArrayMap<IBinder, VisibleActivityInfo> visibleActivityInfoArrayMap =
686                 new ArrayMap<>(count);
687         for (int i = 0; i < count; i++) {
688             ActivityAssistInfo info = allVisibleActivities.get(i);
689             if (DEBUG) {
690                 Slog.d(TAG, "ActivityAssistInfo : activityToken=" + info.getActivityToken()
691                         + ", assistToken=" + info.getAssistToken()
692                         + ", taskId=" + info.getTaskId());
693             }
694             visibleActivityInfoArrayMap.put(info.getActivityToken(),
695                     new VisibleActivityInfo(info.getTaskId(), info.getAssistToken()));
696         }
697         return visibleActivityInfoArrayMap;
698     }
699 
700     // TODO(b/242359988): Split this method up
handleVisibleActivitiesLocked(@onNull IBinder activityToken, int type)701     private void handleVisibleActivitiesLocked(@NonNull IBinder activityToken, int type) {
702         if (DEBUG) {
703             Slog.d(TAG, "handleVisibleActivitiesLocked activityToken=" + activityToken
704                     + ", type=" + type);
705         }
706 
707         if (!mListeningVisibleActivity) {
708             if (DEBUG) {
709                 Slog.d(TAG, "not enable listening visible activity");
710             }
711             return;
712         }
713         if (!mShown || mCanceled || mSession == null) {
714             return;
715         }
716 
717         // We use this local variable to determine to call onVisible or onInvisible.
718         boolean notifyOnVisible = false;
719         VisibleActivityInfo notifyVisibleActivityInfo = null;
720 
721         if (type == VoiceInteractionSession.VOICE_INTERACTION_ACTIVITY_EVENT_START
722                 || type == VoiceInteractionSession.VOICE_INTERACTION_ACTIVITY_EVENT_RESUME) {
723             // It seems that the onStart is unnecessary. But if we have it, the assistant
724             // application can request the directActions early. Even if we have the onStart,
725             // we still need the onResume because it is possible that the activity goes to
726             // onResume from onPause with invisible before the activity goes to onStop from
727             // onPause.
728 
729             // Check if we have reported this activity as visible. If we have reported it as
730             // visible, do nothing.
731             if (mVisibleActivityInfoForToken.containsKey(activityToken)) {
732                 return;
733             }
734 
735             // Before reporting this activity as visible, we need to make sure the activity
736             // is really visible.
737             notifyVisibleActivityInfo = getVisibleActivityInfoFromTopVisibleActivity(
738                     activityToken);
739             if (notifyVisibleActivityInfo == null) {
740                 return;
741             }
742             notifyOnVisible = true;
743         } else if (type == VoiceInteractionSession.VOICE_INTERACTION_ACTIVITY_EVENT_PAUSE) {
744             // For the onPause stage, the Activity is not necessarily invisible now, so we need
745             // to check its state.
746             // Note: After syncing with Activity owner, before the onPause is called, the
747             // visibility state has been updated.
748             notifyVisibleActivityInfo = getVisibleActivityInfoFromTopVisibleActivity(
749                     activityToken);
750             if (notifyVisibleActivityInfo != null) {
751                 return;
752             }
753 
754             // Also make sure we previously reported this Activity as visible.
755             notifyVisibleActivityInfo = mVisibleActivityInfoForToken.get(activityToken);
756             if (notifyVisibleActivityInfo == null) {
757                 return;
758             }
759         } else if (type == VoiceInteractionSession.VOICE_INTERACTION_ACTIVITY_EVENT_STOP) {
760             // For the onStop stage, the activity is in invisible state. We only need to consider if
761             // we have reported this activity as visible. If we have reported it as visible, we
762             // need to report it as invisible.
763             // Why we still need onStop? Because it is possible that the activity is in a visible
764             // state during onPause stage, when the activity enters onStop from onPause, we may
765             // need to notify onInvisible.
766             // Note: After syncing with Activity owner, before the onStop is called, the
767             // visibility state has been updated.
768             notifyVisibleActivityInfo = mVisibleActivityInfoForToken.get(activityToken);
769             if (notifyVisibleActivityInfo == null) {
770                 return;
771             }
772         } else {
773             Slog.w(TAG, "notifyActivityEventChangedLocked unexpected type=" + type);
774             return;
775         }
776 
777         try {
778             mSession.notifyVisibleActivityInfoChanged(notifyVisibleActivityInfo,
779                     notifyOnVisible ? VisibleActivityInfo.TYPE_ACTIVITY_ADDED
780                             : VisibleActivityInfo.TYPE_ACTIVITY_REMOVED);
781         } catch (RemoteException e) {
782             if (DEBUG) {
783                 Slog.w(TAG, "handleVisibleActivitiesLocked RemoteException : " + e);
784             }
785         }
786 
787         if (notifyOnVisible) {
788             mVisibleActivityInfoForToken.put(activityToken, notifyVisibleActivityInfo);
789         } else {
790             mVisibleActivityInfoForToken.remove(activityToken);
791         }
792     }
793 
notifyVisibleActivitiesChangedLocked( ArrayMap<IBinder, VisibleActivityInfo> visibleActivityInfos, int type)794     private void notifyVisibleActivitiesChangedLocked(
795             ArrayMap<IBinder, VisibleActivityInfo> visibleActivityInfos, int type) {
796         if (visibleActivityInfos == null || visibleActivityInfos.isEmpty()) {
797             return;
798         }
799         if (mSession == null) {
800             return;
801         }
802         try {
803             for (int i = 0; i < visibleActivityInfos.size(); i++) {
804                 mSession.notifyVisibleActivityInfoChanged(visibleActivityInfos.valueAt(i), type);
805             }
806         } catch (RemoteException e) {
807             if (DEBUG) {
808                 Slog.w(TAG, "notifyVisibleActivitiesChangedLocked RemoteException : " + e);
809             }
810         }
811         if (DEBUG) {
812             Slog.d(TAG, "notifyVisibleActivitiesChangedLocked type=" + type + ", count="
813                     + visibleActivityInfos.size());
814         }
815     }
816 
getVisibleActivityInfoFromTopVisibleActivity( @onNull IBinder activityToken)817     private VisibleActivityInfo getVisibleActivityInfoFromTopVisibleActivity(
818             @NonNull IBinder activityToken) {
819         final ArrayMap<IBinder, VisibleActivityInfo> visibleActivityInfos =
820                 getTopVisibleActivityInfosLocked();
821         if (visibleActivityInfos == null) {
822             return null;
823         }
824         return visibleActivityInfos.get(activityToken);
825     }
826 
notifyActivityDestroyedLocked(@onNull IBinder activityToken)827     void notifyActivityDestroyedLocked(@NonNull IBinder activityToken) {
828         if (DEBUG) {
829             Slog.d(TAG, "notifyActivityDestroyedLocked activityToken=" + activityToken);
830         }
831         if (!mListeningVisibleActivity) {
832             if (DEBUG) {
833                 Slog.d(TAG, "not enable listening visible activity");
834             }
835             return;
836         }
837         mScheduledExecutorService.execute(() -> {
838             synchronized (mLock) {
839                 if (!mListeningVisibleActivity) {
840                     return;
841                 }
842                 if (!mShown || mCanceled || mSession == null) {
843                     return;
844                 }
845 
846                 VisibleActivityInfo visibleActivityInfo = mVisibleActivityInfoForToken.remove(
847                         activityToken);
848                 if (visibleActivityInfo != null) {
849                     try {
850                         mSession.notifyVisibleActivityInfoChanged(visibleActivityInfo,
851                                 VisibleActivityInfo.TYPE_ACTIVITY_REMOVED);
852                     } catch (RemoteException e) {
853                         if (DEBUG) {
854                             Slog.w(TAG, "notifyVisibleActivityInfoChanged RemoteException : " + e);
855                         }
856                     }
857                 }
858             }
859         });
860     }
861 
removeFromLowPowerStandbyAllowlist()862     private void removeFromLowPowerStandbyAllowlist() {
863         synchronized (mLock) {
864             if (mLowPowerStandbyAllowlisted) {
865                 mFgHandler.removeCallbacks(mRemoveFromLowPowerStandbyAllowlistRunnable);
866                 mLowPowerStandbyControllerInternal.removeFromAllowlist(mCallingUid,
867                         PowerManager.LOW_POWER_STANDBY_ALLOWED_REASON_VOICE_INTERACTION);
868                 mLowPowerStandbyAllowlisted = false;
869             }
870         }
871     }
872 
873     @Override
onServiceConnected(ComponentName name, IBinder service)874     public void onServiceConnected(ComponentName name, IBinder service) {
875         synchronized (mLock) {
876             mService = IVoiceInteractionSessionService.Stub.asInterface(service);
877             if (!mCanceled) {
878                 try {
879                     mService.newSession(mToken, mShowArgs, mShowFlags);
880                 } catch (RemoteException e) {
881                     Slog.w(TAG, "Failed adding window token", e);
882                 }
883             }
884         }
885     }
886 
887     @Override
onServiceDisconnected(ComponentName name)888     public void onServiceDisconnected(ComponentName name) {
889         mCallback.sessionConnectionGone(this);
890         synchronized (mLock) {
891             mService = null;
892         }
893     }
894 
dump(String prefix, PrintWriter pw)895     public void dump(String prefix, PrintWriter pw) {
896         pw.print(prefix); pw.print("mToken="); pw.println(mToken);
897         pw.print(prefix); pw.print("mShown="); pw.println(mShown);
898         pw.print(prefix); pw.print("mShowArgs="); pw.println(mShowArgs);
899         pw.print(prefix); pw.print("mShowFlags=0x"); pw.println(Integer.toHexString(mShowFlags));
900         pw.print(prefix); pw.print("mBound="); pw.println(mBound);
901         if (mBound) {
902             pw.print(prefix); pw.print("mService="); pw.println(mService);
903             pw.print(prefix); pw.print("mSession="); pw.println(mSession);
904             pw.print(prefix); pw.print("mInteractor="); pw.println(mInteractor);
905         }
906         mAssistDataRequester.dump(prefix, pw);
907     }
908 
909     private Runnable mShowAssistDisclosureRunnable = new Runnable() {
910         @Override
911         public void run() {
912             StatusBarManagerInternal statusBarInternal = LocalServices.getService(
913                     StatusBarManagerInternal.class);
914             if (statusBarInternal != null) {
915                 statusBarInternal.showAssistDisclosure();
916             }
917         }
918     };
919 }
920