blob: 90ccec852e1ee080693e4baa65f257bfc8c204e7 [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.voiceinteraction;
import static android.app.AppOpsManager.OP_ASSIST_SCREENSHOT;
import static android.app.AppOpsManager.OP_ASSIST_STRUCTURE;
import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
import static android.content.Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_ACTIVITY_ID;
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT;
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA;
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_TASK_ID;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.app.UriGrantsManager;
import android.app.assist.AssistContent;
import android.app.assist.AssistStructure;
import android.content.ClipData;
import android.content.ComponentName;
import android.content.ContentProvider;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.Bitmap;
import android.hardware.power.Boost;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.PowerManagerInternal;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.voice.IVoiceInteractionSession;
import android.service.voice.IVoiceInteractionSessionService;
import android.service.voice.VisibleActivityInfo;
import android.service.voice.VoiceInteractionService;
import android.service.voice.VoiceInteractionSession;
import android.util.Slog;
import android.view.IWindowManager;
import com.android.internal.app.AssistUtils;
import com.android.internal.app.IVoiceInteractionSessionShowCallback;
import com.android.internal.app.IVoiceInteractor;
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.am.AssistDataRequester;
import com.android.server.am.AssistDataRequester.AssistDataRequesterCallbacks;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.wm.ActivityAssistInfo;
import com.android.server.wm.ActivityTaskManagerInternal;
import java.io.PrintWriter;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
final class VoiceInteractionSessionConnection implements ServiceConnection,
AssistDataRequesterCallbacks {
static final String TAG = "VoiceInteractionServiceManager";
static final boolean DEBUG = false;
static final int POWER_BOOST_TIMEOUT_MS = Integer.parseInt(
System.getProperty("vendor.powerhal.interaction.max", "200"));
static final int BOOST_TIMEOUT_MS = 300;
// TODO: To avoid ap doesn't call hide, only 10 secs for now, need a better way to manage it
// in the future.
static final int MAX_POWER_BOOST_TIMEOUT = 10_000;
final IBinder mToken = new Binder();
final Object mLock;
final ComponentName mSessionComponentName;
final Intent mBindIntent;
final int mUser;
final Context mContext;
final Callback mCallback;
final int mCallingUid;
final Handler mHandler;
final IActivityManager mAm;
final UriGrantsManagerInternal mUgmInternal;
final IWindowManager mIWindowManager;
final AppOpsManager mAppOps;
final IBinder mPermissionOwner;
boolean mShown;
Bundle mShowArgs;
int mShowFlags;
boolean mBound;
boolean mFullyBound;
boolean mCanceled;
IVoiceInteractionSessionService mService;
IVoiceInteractionSession mSession;
IVoiceInteractor mInteractor;
ArrayList<IVoiceInteractionSessionShowCallback> mPendingShowCallbacks = new ArrayList<>();
private List<ActivityAssistInfo> mPendingHandleAssistWithoutData = new ArrayList<>();
AssistDataRequester mAssistDataRequester;
private boolean mListeningVisibleActivity;
private final ScheduledExecutorService mScheduledExecutorService =
Executors.newSingleThreadScheduledExecutor();
private final List<VisibleActivityInfo> mVisibleActivityInfos = new ArrayList<>();
private final PowerManagerInternal mPowerManagerInternal;
private PowerBoostSetter mSetPowerBoostRunnable;
private final Handler mFgHandler;
class PowerBoostSetter implements Runnable {
private boolean mCanceled;
private final Instant mExpiryTime;
PowerBoostSetter(Instant expiryTime) {
mExpiryTime = expiryTime;
}
@Override
public void run() {
synchronized (mLock) {
if (mCanceled) {
return;
}
// To avoid voice interaction service does not call hide to cancel setting
// power boost. We will cancel set boost when reaching the max timeout.
if (Instant.now().isBefore(mExpiryTime)) {
mPowerManagerInternal.setPowerBoost(Boost.INTERACTION, BOOST_TIMEOUT_MS);
if (mSetPowerBoostRunnable != null) {
mFgHandler.postDelayed(mSetPowerBoostRunnable, POWER_BOOST_TIMEOUT_MS);
}
} else {
Slog.w(TAG, "Reset power boost INTERACTION because reaching max timeout.");
mPowerManagerInternal.setPowerBoost(Boost.INTERACTION, /* durationMs */ -1);
}
}
}
void cancel() {
synchronized (mLock) {
mCanceled = true;
}
}
}
IVoiceInteractionSessionShowCallback mShowCallback =
new IVoiceInteractionSessionShowCallback.Stub() {
@Override
public void onFailed() throws RemoteException {
synchronized (mLock) {
notifyPendingShowCallbacksFailedLocked();
}
}
@Override
public void onShown() throws RemoteException {
synchronized (mLock) {
// TODO: Figure out whether this is good enough or whether we need to hook into
// Window manager to actually wait for the window to be drawn.
notifyPendingShowCallbacksShownLocked();
}
}
};
public interface Callback {
public void sessionConnectionGone(VoiceInteractionSessionConnection connection);
public void onSessionShown(VoiceInteractionSessionConnection connection);
public void onSessionHidden(VoiceInteractionSessionConnection connection);
}
final ServiceConnection mFullConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
public VoiceInteractionSessionConnection(Object lock, ComponentName component, int user,
Context context, Callback callback, int callingUid, Handler handler) {
mLock = lock;
mSessionComponentName = component;
mUser = user;
mContext = context;
mCallback = callback;
mCallingUid = callingUid;
mHandler = handler;
mAm = ActivityManager.getService();
mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
mIWindowManager = IWindowManager.Stub.asInterface(
ServiceManager.getService(Context.WINDOW_SERVICE));
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
mAppOps = context.getSystemService(AppOpsManager.class);
mFgHandler = FgThread.getHandler();
mAssistDataRequester = new AssistDataRequester(mContext, mIWindowManager,
(AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE),
this, mLock, OP_ASSIST_STRUCTURE, OP_ASSIST_SCREENSHOT);
final IBinder permOwner = mUgmInternal.newUriPermissionOwner("voicesession:"
+ component.flattenToShortString());
mPermissionOwner = permOwner;
mBindIntent = new Intent(VoiceInteractionService.SERVICE_INTERFACE);
mBindIntent.setComponent(mSessionComponentName);
mBound = mContext.bindServiceAsUser(mBindIntent, this,
Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY
| Context.BIND_ALLOW_OOM_MANAGEMENT
| Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS, new UserHandle(mUser));
if (mBound) {
try {
mIWindowManager.addWindowToken(mToken, TYPE_VOICE_INTERACTION, DEFAULT_DISPLAY,
null /* options */);
} catch (RemoteException e) {
Slog.w(TAG, "Failed adding window token", e);
}
} else {
Slog.w(TAG, "Failed binding to voice interaction session service "
+ mSessionComponentName);
}
}
public int getUserDisabledShowContextLocked() {
int flags = 0;
if (Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.ASSIST_STRUCTURE_ENABLED, 1, mUser) == 0) {
flags |= VoiceInteractionSession.SHOW_WITH_ASSIST;
}
if (Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.ASSIST_SCREENSHOT_ENABLED, 1, mUser) == 0) {
flags |= VoiceInteractionSession.SHOW_WITH_SCREENSHOT;
}
return flags;
}
public boolean showLocked(Bundle args, int flags, int disabledContext,
IVoiceInteractionSessionShowCallback showCallback,
List<ActivityAssistInfo> topActivities) {
if (mBound) {
if (!mFullyBound) {
mFullyBound = mContext.bindServiceAsUser(mBindIntent, mFullConnection,
Context.BIND_AUTO_CREATE | Context.BIND_TREAT_LIKE_ACTIVITY
| Context.BIND_SCHEDULE_LIKE_TOP_APP
| Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS,
new UserHandle(mUser));
}
mShown = true;
mShowArgs = args;
mShowFlags = flags;
disabledContext |= getUserDisabledShowContextLocked();
boolean fetchData = (flags & VoiceInteractionSession.SHOW_WITH_ASSIST) != 0;
boolean fetchScreenshot = (flags & VoiceInteractionSession.SHOW_WITH_SCREENSHOT) != 0;
boolean assistDataRequestNeeded = fetchData || fetchScreenshot;
if (assistDataRequestNeeded) {
int topActivitiesCount = topActivities.size();
final ArrayList<IBinder> topActivitiesToken = new ArrayList<>(topActivitiesCount);
for (int i = 0; i < topActivitiesCount; i++) {
topActivitiesToken.add(topActivities.get(i).getActivityToken());
}
mAssistDataRequester.requestAssistData(topActivitiesToken,
fetchData,
fetchScreenshot,
(disabledContext & VoiceInteractionSession.SHOW_WITH_ASSIST) == 0,
(disabledContext & VoiceInteractionSession.SHOW_WITH_SCREENSHOT) == 0,
mCallingUid, mSessionComponentName.getPackageName());
boolean needDisclosure = mAssistDataRequester.getPendingDataCount() > 0
|| mAssistDataRequester.getPendingScreenshotCount() > 0;
if (needDisclosure && AssistUtils.shouldDisclose(mContext, mSessionComponentName)) {
mHandler.post(mShowAssistDisclosureRunnable);
}
}
if (mSession != null) {
try {
mSession.show(mShowArgs, mShowFlags, showCallback);
mShowArgs = null;
mShowFlags = 0;
} catch (RemoteException e) {
}
if (assistDataRequestNeeded) {
mAssistDataRequester.processPendingAssistData();
} else {
doHandleAssistWithoutData(topActivities);
}
} else {
if (showCallback != null) {
mPendingShowCallbacks.add(showCallback);
}
if (!assistDataRequestNeeded) {
// If no data are required we are not passing trough mAssistDataRequester. As
// a consequence, when a new session is delivered it is needed to process those
// requests manually.
mPendingHandleAssistWithoutData = topActivities;
}
}
// remove if already existing one.
if (mSetPowerBoostRunnable != null) {
mSetPowerBoostRunnable.cancel();
}
mSetPowerBoostRunnable = new PowerBoostSetter(
Instant.now().plusMillis(MAX_POWER_BOOST_TIMEOUT));
mFgHandler.post(mSetPowerBoostRunnable);
mCallback.onSessionShown(this);
return true;
}
if (showCallback != null) {
try {
showCallback.onFailed();
} catch (RemoteException e) {
}
}
return false;
}
private void doHandleAssistWithoutData(List<ActivityAssistInfo> topActivities) {
final int activityCount = topActivities.size();
for (int i = 0; i < activityCount; i++) {
final ActivityAssistInfo topActivity = topActivities.get(i);
final IBinder assistToken = topActivity.getAssistToken();
final int taskId = topActivity.getTaskId();
final int activityIndex = i;
try {
mSession.handleAssist(
taskId,
assistToken,
/* assistData = */ null,
/* assistStructure = */ null,
/* assistContent = */ null,
activityIndex,
activityCount);
} catch (RemoteException e) {
// Ignore
}
}
}
@Override
public boolean canHandleReceivedAssistDataLocked() {
return mSession != null;
}
@Override
public void onAssistDataReceivedLocked(Bundle data, int activityIndex, int activityCount) {
// Return early if we have no session
if (mSession == null) {
return;
}
if (data == null) {
try {
mSession.handleAssist(-1, null, null, null, null, 0, 0);
} catch (RemoteException e) {
// Ignore
}
} else {
final int taskId = data.getInt(ASSIST_TASK_ID);
final IBinder activityId = data.getBinder(ASSIST_ACTIVITY_ID);
final Bundle assistData = data.getBundle(ASSIST_KEY_DATA);
final AssistStructure structure = data.getParcelable(ASSIST_KEY_STRUCTURE);
final AssistContent content = data.getParcelable(ASSIST_KEY_CONTENT);
int uid = -1;
if (assistData != null) {
uid = assistData.getInt(Intent.EXTRA_ASSIST_UID, -1);
}
if (uid >= 0 && content != null) {
Intent intent = content.getIntent();
if (intent != null) {
ClipData clipData = intent.getClipData();
if (clipData != null && Intent.isAccessUriMode(intent.getFlags())) {
grantClipDataPermissions(clipData, intent.getFlags(), uid,
mCallingUid, mSessionComponentName.getPackageName());
}
}
ClipData clipData = content.getClipData();
if (clipData != null) {
grantClipDataPermissions(clipData, FLAG_GRANT_READ_URI_PERMISSION,
uid, mCallingUid, mSessionComponentName.getPackageName());
}
}
try {
mSession.handleAssist(taskId, activityId, assistData, structure,
content, activityIndex, activityCount);
} catch (RemoteException e) {
// Ignore
}
}
}
@Override
public void onAssistScreenshotReceivedLocked(Bitmap screenshot) {
// Return early if we have no session
if (mSession == null) {
return;
}
try {
mSession.handleScreenshot(screenshot);
} catch (RemoteException e) {
// Ignore
}
}
void grantUriPermission(Uri uri, int mode, int srcUid, int destUid, String destPkg) {
if (!"content".equals(uri.getScheme())) {
return;
}
final long ident = Binder.clearCallingIdentity();
try {
// This will throw SecurityException for us.
mUgmInternal.checkGrantUriPermission(srcUid, null,
ContentProvider.getUriWithoutUserId(uri), mode,
ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(srcUid)));
// No security exception, do the grant.
int sourceUserId = ContentProvider.getUserIdFromUri(uri, mUser);
uri = ContentProvider.getUriWithoutUserId(uri);
UriGrantsManager.getService().grantUriPermissionFromOwner(mPermissionOwner, srcUid,
destPkg, uri, FLAG_GRANT_READ_URI_PERMISSION, sourceUserId, mUser);
} catch (RemoteException e) {
} catch (SecurityException e) {
Slog.w(TAG, "Can't propagate permission", e);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
void grantClipDataItemPermission(ClipData.Item item, int mode, int srcUid, int destUid,
String destPkg) {
if (item.getUri() != null) {
grantUriPermission(item.getUri(), mode, srcUid, destUid, destPkg);
}
Intent intent = item.getIntent();
if (intent != null && intent.getData() != null) {
grantUriPermission(intent.getData(), mode, srcUid, destUid, destPkg);
}
}
void grantClipDataPermissions(ClipData data, int mode, int srcUid, int destUid,
String destPkg) {
final int N = data.getItemCount();
for (int i=0; i<N; i++) {
grantClipDataItemPermission(data.getItemAt(i), mode, srcUid, destUid, destPkg);
}
}
public boolean hideLocked() {
if (mBound) {
if (mShown) {
mShown = false;
mShowArgs = null;
mShowFlags = 0;
mAssistDataRequester.cancel();
mPendingShowCallbacks.clear();
if (mSession != null) {
try {
mSession.hide();
} catch (RemoteException e) {
}
}
mUgmInternal.revokeUriPermissionFromOwner(mPermissionOwner, null,
FLAG_GRANT_READ_URI_PERMISSION | FLAG_GRANT_WRITE_URI_PERMISSION, mUser);
if (mSession != null) {
try {
ActivityTaskManager.getService().finishVoiceTask(mSession);
} catch (RemoteException e) {
}
}
if (mSetPowerBoostRunnable != null) {
mSetPowerBoostRunnable.cancel();
mSetPowerBoostRunnable = null;
}
// A negative value indicates canceling previous boost.
mPowerManagerInternal.setPowerBoost(Boost.INTERACTION, /* durationMs */ -1);
mCallback.onSessionHidden(this);
}
if (mFullyBound) {
mContext.unbindService(mFullConnection);
mFullyBound = false;
}
return true;
}
return false;
}
public void cancelLocked(boolean finishTask) {
mListeningVisibleActivity = false;
mVisibleActivityInfos.clear();
hideLocked();
mCanceled = true;
if (mBound) {
if (mSession != null) {
try {
mSession.destroy();
} catch (RemoteException e) {
Slog.w(TAG, "Voice interation session already dead");
}
}
if (finishTask && mSession != null) {
try {
ActivityTaskManager.getService().finishVoiceTask(mSession);
} catch (RemoteException e) {
}
}
mContext.unbindService(this);
try {
mIWindowManager.removeWindowToken(mToken, DEFAULT_DISPLAY);
} catch (RemoteException e) {
Slog.w(TAG, "Failed removing window token", e);
}
mBound = false;
mService = null;
mSession = null;
mInteractor = null;
}
if (mFullyBound) {
mContext.unbindService(mFullConnection);
mFullyBound = false;
}
}
public boolean deliverNewSessionLocked(IVoiceInteractionSession session,
IVoiceInteractor interactor) {
mSession = session;
mInteractor = interactor;
if (mShown) {
try {
session.show(mShowArgs, mShowFlags, mShowCallback);
mShowArgs = null;
mShowFlags = 0;
} catch (RemoteException e) {
}
mAssistDataRequester.processPendingAssistData();
if (!mPendingHandleAssistWithoutData.isEmpty()) {
doHandleAssistWithoutData(mPendingHandleAssistWithoutData);
mPendingHandleAssistWithoutData.clear();
}
}
return true;
}
private void notifyPendingShowCallbacksShownLocked() {
for (int i = 0; i < mPendingShowCallbacks.size(); i++) {
try {
mPendingShowCallbacks.get(i).onShown();
} catch (RemoteException e) {
}
}
mPendingShowCallbacks.clear();
}
private void notifyPendingShowCallbacksFailedLocked() {
for (int i = 0; i < mPendingShowCallbacks.size(); i++) {
try {
mPendingShowCallbacks.get(i).onFailed();
} catch (RemoteException e) {
}
}
mPendingShowCallbacks.clear();
}
void startListeningVisibleActivityChangedLocked() {
if (DEBUG) {
Slog.d(TAG, "startListeningVisibleActivityChangedLocked");
}
mListeningVisibleActivity = true;
mVisibleActivityInfos.clear();
mScheduledExecutorService.execute(() -> {
if (DEBUG) {
Slog.d(TAG, "call updateVisibleActivitiesLocked from enable listening");
}
synchronized (mLock) {
updateVisibleActivitiesLocked();
}
});
}
void stopListeningVisibleActivityChangedLocked() {
if (DEBUG) {
Slog.d(TAG, "stopListeningVisibleActivityChangedLocked");
}
mListeningVisibleActivity = false;
mVisibleActivityInfos.clear();
}
void notifyActivityEventChangedLocked() {
if (DEBUG) {
Slog.d(TAG, "notifyActivityEventChangedLocked");
}
if (!mListeningVisibleActivity) {
if (DEBUG) {
Slog.d(TAG, "not enable listening visible activity");
}
return;
}
mScheduledExecutorService.execute(() -> {
if (DEBUG) {
Slog.d(TAG, "call updateVisibleActivitiesLocked from activity event");
}
synchronized (mLock) {
updateVisibleActivitiesLocked();
}
});
}
private List<VisibleActivityInfo> getVisibleActivityInfosLocked() {
if (DEBUG) {
Slog.d(TAG, "getVisibleActivityInfosLocked");
}
List<ActivityAssistInfo> allVisibleActivities =
LocalServices.getService(ActivityTaskManagerInternal.class)
.getTopVisibleActivities();
if (DEBUG) {
Slog.d(TAG,
"getVisibleActivityInfosLocked: allVisibleActivities=" + allVisibleActivities);
}
if (allVisibleActivities == null || allVisibleActivities.isEmpty()) {
Slog.w(TAG, "no visible activity");
return null;
}
final int count = allVisibleActivities.size();
final List<VisibleActivityInfo> visibleActivityInfos = new ArrayList<>(count);
for (int i = 0; i < count; i++) {
ActivityAssistInfo info = allVisibleActivities.get(i);
if (DEBUG) {
Slog.d(TAG, " : activityToken=" + info.getActivityToken()
+ ", assistToken=" + info.getAssistToken()
+ ", taskId=" + info.getTaskId());
}
visibleActivityInfos.add(
new VisibleActivityInfo(info.getTaskId(), info.getAssistToken()));
}
return visibleActivityInfos;
}
private void updateVisibleActivitiesLocked() {
if (DEBUG) {
Slog.d(TAG, "updateVisibleActivitiesLocked");
}
if (mSession == null) {
return;
}
if (!mShown || !mListeningVisibleActivity || mCanceled) {
return;
}
final List<VisibleActivityInfo> newVisibleActivityInfos = getVisibleActivityInfosLocked();
if (newVisibleActivityInfos == null || newVisibleActivityInfos.isEmpty()) {
updateVisibleActivitiesChangedLocked(mVisibleActivityInfos,
VisibleActivityInfo.TYPE_ACTIVITY_REMOVED);
mVisibleActivityInfos.clear();
return;
}
if (mVisibleActivityInfos.isEmpty()) {
updateVisibleActivitiesChangedLocked(newVisibleActivityInfos,
VisibleActivityInfo.TYPE_ACTIVITY_ADDED);
mVisibleActivityInfos.addAll(newVisibleActivityInfos);
return;
}
final List<VisibleActivityInfo> addedActivities = new ArrayList<>();
final List<VisibleActivityInfo> removedActivities = new ArrayList<>();
removedActivities.addAll(mVisibleActivityInfos);
for (int i = 0; i < newVisibleActivityInfos.size(); i++) {
final VisibleActivityInfo candidateVisibleActivityInfo = newVisibleActivityInfos.get(i);
if (!removedActivities.isEmpty() && removedActivities.contains(
candidateVisibleActivityInfo)) {
removedActivities.remove(candidateVisibleActivityInfo);
} else {
addedActivities.add(candidateVisibleActivityInfo);
}
}
if (!addedActivities.isEmpty()) {
updateVisibleActivitiesChangedLocked(addedActivities,
VisibleActivityInfo.TYPE_ACTIVITY_ADDED);
}
if (!removedActivities.isEmpty()) {
updateVisibleActivitiesChangedLocked(removedActivities,
VisibleActivityInfo.TYPE_ACTIVITY_REMOVED);
}
mVisibleActivityInfos.clear();
mVisibleActivityInfos.addAll(newVisibleActivityInfos);
}
private void updateVisibleActivitiesChangedLocked(
List<VisibleActivityInfo> visibleActivityInfos, int type) {
if (visibleActivityInfos == null || visibleActivityInfos.isEmpty()) {
return;
}
if (mSession == null) {
return;
}
try {
for (int i = 0; i < visibleActivityInfos.size(); i++) {
mSession.updateVisibleActivityInfo(visibleActivityInfos.get(i), type);
}
} catch (RemoteException e) {
if (DEBUG) {
Slog.w(TAG, "updateVisibleActivitiesChangedLocked RemoteException : " + e);
}
}
if (DEBUG) {
Slog.d(TAG, "updateVisibleActivitiesChangedLocked type=" + type + ", count="
+ visibleActivityInfos.size());
}
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
synchronized (mLock) {
mService = IVoiceInteractionSessionService.Stub.asInterface(service);
if (!mCanceled) {
try {
mService.newSession(mToken, mShowArgs, mShowFlags);
} catch (RemoteException e) {
Slog.w(TAG, "Failed adding window token", e);
}
}
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
mCallback.sessionConnectionGone(this);
synchronized (mLock) {
mService = null;
}
}
public void dump(String prefix, PrintWriter pw) {
pw.print(prefix); pw.print("mToken="); pw.println(mToken);
pw.print(prefix); pw.print("mShown="); pw.println(mShown);
pw.print(prefix); pw.print("mShowArgs="); pw.println(mShowArgs);
pw.print(prefix); pw.print("mShowFlags=0x"); pw.println(Integer.toHexString(mShowFlags));
pw.print(prefix); pw.print("mBound="); pw.println(mBound);
if (mBound) {
pw.print(prefix); pw.print("mService="); pw.println(mService);
pw.print(prefix); pw.print("mSession="); pw.println(mSession);
pw.print(prefix); pw.print("mInteractor="); pw.println(mInteractor);
}
mAssistDataRequester.dump(prefix, pw);
}
private Runnable mShowAssistDisclosureRunnable = new Runnable() {
@Override
public void run() {
StatusBarManagerInternal statusBarInternal = LocalServices.getService(
StatusBarManagerInternal.class);
if (statusBarInternal != null) {
statusBarInternal.showAssistDisclosure();
}
}
};
};