109eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley/*
209eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley * Copyright (C) 2015 The Android Open Source Project
309eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley *
409eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley * Licensed under the Apache License, Version 2.0 (the "License");
509eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley * you may not use this file except in compliance with the License.
609eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley * You may obtain a copy of the License at
709eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley *
809eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley *      http://www.apache.org/licenses/LICENSE-2.0
909eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley *
1009eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley * Unless required by applicable law or agreed to in writing, software
1109eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley * distributed under the License is distributed on an "AS IS" BASIS,
1209eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1309eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley * See the License for the specific language governing permissions and
1409eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley * limitations under the License.
1509eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley */
1609eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley
1709eb749704afd9e226e1347cb20c90be2016cd21Christopher Wileypackage com.android.server.voiceinteraction;
1809eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley
1909eb749704afd9e226e1347cb20c90be2016cd21Christopher Wileyimport android.app.ActivityManager;
2009eb749704afd9e226e1347cb20c90be2016cd21Christopher Wileyimport android.app.ActivityManagerNative;
2109eb749704afd9e226e1347cb20c90be2016cd21Christopher Wileyimport android.app.AppOpsManager;
22cff7f175c1a4f790fdc64a56695c5b4b08b6bb6eChristopher Wileyimport android.app.IActivityManager;
23cff7f175c1a4f790fdc64a56695c5b4b08b6bb6eChristopher Wileyimport android.app.assist.AssistContent;
24cff7f175c1a4f790fdc64a56695c5b4b08b6bb6eChristopher Wileyimport android.app.assist.AssistStructure;
25cff7f175c1a4f790fdc64a56695c5b4b08b6bb6eChristopher Wileyimport android.content.ClipData;
26cff7f175c1a4f790fdc64a56695c5b4b08b6bb6eChristopher Wileyimport android.content.ComponentName;
27c1e491d5a4923298b612de919537d4293574b443Christopher Wileyimport android.content.ContentProvider;
28cff7f175c1a4f790fdc64a56695c5b4b08b6bb6eChristopher Wileyimport android.content.Context;
29cff7f175c1a4f790fdc64a56695c5b4b08b6bb6eChristopher Wileyimport android.content.Intent;
30cff7f175c1a4f790fdc64a56695c5b4b08b6bb6eChristopher Wileyimport android.content.ServiceConnection;
31cff7f175c1a4f790fdc64a56695c5b4b08b6bb6eChristopher Wileyimport android.graphics.Bitmap;
32c1e491d5a4923298b612de919537d4293574b443Christopher Wileyimport android.net.Uri;
33c1e491d5a4923298b612de919537d4293574b443Christopher Wileyimport android.os.Binder;
34c1e491d5a4923298b612de919537d4293574b443Christopher Wileyimport android.os.Bundle;
35c1e491d5a4923298b612de919537d4293574b443Christopher Wileyimport android.os.Handler;
36c1e491d5a4923298b612de919537d4293574b443Christopher Wileyimport android.os.IBinder;
37c1e491d5a4923298b612de919537d4293574b443Christopher Wileyimport android.os.RemoteException;
38c1e491d5a4923298b612de919537d4293574b443Christopher Wileyimport android.os.ServiceManager;
39c1e491d5a4923298b612de919537d4293574b443Christopher Wileyimport android.os.UserHandle;
40c1e491d5a4923298b612de919537d4293574b443Christopher Wileyimport android.provider.Settings;
41c1e491d5a4923298b612de919537d4293574b443Christopher Wileyimport android.service.voice.IVoiceInteractionSession;
4209eb749704afd9e226e1347cb20c90be2016cd21Christopher Wileyimport android.service.voice.IVoiceInteractionSessionService;
4309eb749704afd9e226e1347cb20c90be2016cd21Christopher Wileyimport android.service.voice.VoiceInteractionService;
4409eb749704afd9e226e1347cb20c90be2016cd21Christopher Wileyimport android.service.voice.VoiceInteractionSession;
4509eb749704afd9e226e1347cb20c90be2016cd21Christopher Wileyimport android.util.Slog;
4609eb749704afd9e226e1347cb20c90be2016cd21Christopher Wileyimport android.view.IWindowManager;
4709eb749704afd9e226e1347cb20c90be2016cd21Christopher Wileyimport android.view.WindowManager;
4809eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley
4909eb749704afd9e226e1347cb20c90be2016cd21Christopher Wileyimport com.android.internal.app.IAssistScreenshotReceiver;
50c1e491d5a4923298b612de919537d4293574b443Christopher Wileyimport com.android.internal.app.IVoiceInteractionSessionShowCallback;
51cff7f175c1a4f790fdc64a56695c5b4b08b6bb6eChristopher Wileyimport com.android.internal.app.IVoiceInteractor;
52c1e491d5a4923298b612de919537d4293574b443Christopher Wileyimport com.android.internal.logging.MetricsLogger;
53c1e491d5a4923298b612de919537d4293574b443Christopher Wileyimport com.android.internal.os.IResultReceiver;
54c1e491d5a4923298b612de919537d4293574b443Christopher Wileyimport com.android.server.LocalServices;
55c1e491d5a4923298b612de919537d4293574b443Christopher Wileyimport com.android.server.statusbar.StatusBarManagerInternal;
56c1e491d5a4923298b612de919537d4293574b443Christopher Wiley
5709eb749704afd9e226e1347cb20c90be2016cd21Christopher Wileyimport java.io.PrintWriter;
5809eb749704afd9e226e1347cb20c90be2016cd21Christopher Wileyimport java.util.ArrayList;
5909eb749704afd9e226e1347cb20c90be2016cd21Christopher Wileyimport java.util.List;
6009eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley
6109eb749704afd9e226e1347cb20c90be2016cd21Christopher Wileyfinal class VoiceInteractionSessionConnection implements ServiceConnection {
6209eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley
6309eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley    final static String TAG = "VoiceInteractionServiceManager";
6409eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley
6509eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley    private static final String KEY_RECEIVER_EXTRA_COUNT = "count";
6609eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley    private static final String KEY_RECEIVER_EXTRA_INDEX = "index";
6709eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley
6809eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley    final IBinder mToken = new Binder();
6909eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley    final Object mLock;
7009eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley    final ComponentName mSessionComponentName;
7109eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley    final Intent mBindIntent;
7209eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley    final int mUser;
7309eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley    final Context mContext;
7409eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley    final Callback mCallback;
7509eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley    final int mCallingUid;
7609eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley    final Handler mHandler;
7709eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley    final IActivityManager mAm;
7809eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley    final IWindowManager mIWindowManager;
7909eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley    final AppOpsManager mAppOps;
8009eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley    final IBinder mPermissionOwner;
8109eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley    boolean mShown;
8209eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley    Bundle mShowArgs;
8309eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley    int mShowFlags;
8409eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley    boolean mBound;
8509eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley    boolean mFullyBound;
8609eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley    boolean mCanceled;
87cff7f175c1a4f790fdc64a56695c5b4b08b6bb6eChristopher Wiley    IVoiceInteractionSessionService mService;
88cff7f175c1a4f790fdc64a56695c5b4b08b6bb6eChristopher Wiley    IVoiceInteractionSession mSession;
89cff7f175c1a4f790fdc64a56695c5b4b08b6bb6eChristopher Wiley    IVoiceInteractor mInteractor;
90cff7f175c1a4f790fdc64a56695c5b4b08b6bb6eChristopher Wiley    boolean mHaveAssistData;
91cff7f175c1a4f790fdc64a56695c5b4b08b6bb6eChristopher Wiley    int mPendingAssistDataCount;
92cff7f175c1a4f790fdc64a56695c5b4b08b6bb6eChristopher Wiley    ArrayList<AssistDataForActivity> mAssistData = new ArrayList<>();
93cff7f175c1a4f790fdc64a56695c5b4b08b6bb6eChristopher Wiley    boolean mHaveScreenshot;
9409eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley    Bitmap mScreenshot;
95c1e491d5a4923298b612de919537d4293574b443Christopher Wiley    ArrayList<IVoiceInteractionSessionShowCallback> mPendingShowCallbacks = new ArrayList<>();
96c1e491d5a4923298b612de919537d4293574b443Christopher Wiley
97c1e491d5a4923298b612de919537d4293574b443Christopher Wiley    static class AssistDataForActivity {
98c1e491d5a4923298b612de919537d4293574b443Christopher Wiley        int activityIndex;
99c1e491d5a4923298b612de919537d4293574b443Christopher Wiley        int activityCount;
100c1e491d5a4923298b612de919537d4293574b443Christopher Wiley        Bundle data;
101c1e491d5a4923298b612de919537d4293574b443Christopher Wiley
102c1e491d5a4923298b612de919537d4293574b443Christopher Wiley        public AssistDataForActivity(Bundle data) {
10309eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley            this.data = data;
10409eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley            Bundle receiverExtras = data.getBundle(VoiceInteractionSession.KEY_RECEIVER_EXTRAS);
10509eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley            if (receiverExtras != null) {
10609eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley                activityIndex = receiverExtras.getInt(KEY_RECEIVER_EXTRA_INDEX);
107cff7f175c1a4f790fdc64a56695c5b4b08b6bb6eChristopher Wiley                activityCount = receiverExtras.getInt(KEY_RECEIVER_EXTRA_COUNT);
108cff7f175c1a4f790fdc64a56695c5b4b08b6bb6eChristopher Wiley            }
109cff7f175c1a4f790fdc64a56695c5b4b08b6bb6eChristopher Wiley        }
110cff7f175c1a4f790fdc64a56695c5b4b08b6bb6eChristopher Wiley    }
111cff7f175c1a4f790fdc64a56695c5b4b08b6bb6eChristopher Wiley
112cff7f175c1a4f790fdc64a56695c5b4b08b6bb6eChristopher Wiley    IVoiceInteractionSessionShowCallback mShowCallback =
11309eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley            new IVoiceInteractionSessionShowCallback.Stub() {
11409eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley        @Override
11509eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley        public void onFailed() throws RemoteException {
11609eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley            synchronized (mLock) {
11709eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley                notifyPendingShowCallbacksFailedLocked();
11809eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley            }
11909eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley        }
120c1e491d5a4923298b612de919537d4293574b443Christopher Wiley
121c1e491d5a4923298b612de919537d4293574b443Christopher Wiley        @Override
122c1e491d5a4923298b612de919537d4293574b443Christopher Wiley        public void onShown() throws RemoteException {
123c1e491d5a4923298b612de919537d4293574b443Christopher Wiley            synchronized (mLock) {
124c1e491d5a4923298b612de919537d4293574b443Christopher Wiley                // TODO: Figure out whether this is good enough or whether we need to hook into
12509eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley                // Window manager to actually wait for the window to be drawn.
12609eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley                notifyPendingShowCallbacksShownLocked();
12709eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley            }
12809eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley        }
12909eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley    };
130cff7f175c1a4f790fdc64a56695c5b4b08b6bb6eChristopher Wiley
13109eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley    public interface Callback {
13209eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley        public void sessionConnectionGone(VoiceInteractionSessionConnection connection);
13309eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley    }
134c1e491d5a4923298b612de919537d4293574b443Christopher Wiley
135c1e491d5a4923298b612de919537d4293574b443Christopher Wiley    final ServiceConnection mFullConnection = new ServiceConnection() {
136c1e491d5a4923298b612de919537d4293574b443Christopher Wiley        @Override
137c1e491d5a4923298b612de919537d4293574b443Christopher Wiley        public void onServiceConnected(ComponentName name, IBinder service) {
138c1e491d5a4923298b612de919537d4293574b443Christopher Wiley        }
139c1e491d5a4923298b612de919537d4293574b443Christopher Wiley        @Override
140c1e491d5a4923298b612de919537d4293574b443Christopher Wiley        public void onServiceDisconnected(ComponentName name) {
141c1e491d5a4923298b612de919537d4293574b443Christopher Wiley        }
142c1e491d5a4923298b612de919537d4293574b443Christopher Wiley    };
143c1e491d5a4923298b612de919537d4293574b443Christopher Wiley
144c1e491d5a4923298b612de919537d4293574b443Christopher Wiley    final IResultReceiver mAssistReceiver = new IResultReceiver.Stub() {
14509eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley        @Override
14609eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley        public void send(int resultCode, Bundle resultData) throws RemoteException {
14709eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley            synchronized (mLock) {
14809eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley                if (mShown) {
14909eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley                    mHaveAssistData = true;
15009eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley                    mAssistData.add(new AssistDataForActivity(resultData));
151c1e491d5a4923298b612de919537d4293574b443Christopher Wiley                    deliverSessionDataLocked();
152c1e491d5a4923298b612de919537d4293574b443Christopher Wiley                }
153cff7f175c1a4f790fdc64a56695c5b4b08b6bb6eChristopher Wiley            }
154cff7f175c1a4f790fdc64a56695c5b4b08b6bb6eChristopher Wiley        }
15509eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley    };
15609eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley
15709eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley    final IAssistScreenshotReceiver mScreenshotReceiver = new IAssistScreenshotReceiver.Stub() {
15809eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley        @Override
15909eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley        public void send(Bitmap screenshot) throws RemoteException {
16009eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley            synchronized (mLock) {
16109eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley                if (mShown) {
16209eb749704afd9e226e1347cb20c90be2016cd21Christopher Wiley                    mHaveScreenshot = true;
163                    mScreenshot = screenshot;
164                    deliverSessionDataLocked();
165                }
166            }
167        }
168    };
169
170    public VoiceInteractionSessionConnection(Object lock, ComponentName component, int user,
171            Context context, Callback callback, int callingUid, Handler handler) {
172        mLock = lock;
173        mSessionComponentName = component;
174        mUser = user;
175        mContext = context;
176        mCallback = callback;
177        mCallingUid = callingUid;
178        mHandler = handler;
179        mAm = ActivityManagerNative.getDefault();
180        mIWindowManager = IWindowManager.Stub.asInterface(
181                ServiceManager.getService(Context.WINDOW_SERVICE));
182        mAppOps = context.getSystemService(AppOpsManager.class);
183        IBinder permOwner = null;
184        try {
185            permOwner = mAm.newUriPermissionOwner("voicesession:"
186                    + component.flattenToShortString());
187        } catch (RemoteException e) {
188            Slog.w("voicesession", "AM dead", e);
189        }
190        mPermissionOwner = permOwner;
191        mBindIntent = new Intent(VoiceInteractionService.SERVICE_INTERFACE);
192        mBindIntent.setComponent(mSessionComponentName);
193        mBound = mContext.bindServiceAsUser(mBindIntent, this,
194                Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY
195                        | Context.BIND_ALLOW_OOM_MANAGEMENT, new UserHandle(mUser));
196        if (mBound) {
197            try {
198                mIWindowManager.addWindowToken(mToken,
199                        WindowManager.LayoutParams.TYPE_VOICE_INTERACTION);
200            } catch (RemoteException e) {
201                Slog.w(TAG, "Failed adding window token", e);
202            }
203        } else {
204            Slog.w(TAG, "Failed binding to voice interaction session service "
205                    + mSessionComponentName);
206        }
207    }
208
209    public int getUserDisabledShowContextLocked() {
210        int flags = 0;
211        if (Settings.Secure.getIntForUser(mContext.getContentResolver(),
212                Settings.Secure.ASSIST_STRUCTURE_ENABLED, 1, mUser) == 0) {
213            flags |= VoiceInteractionSession.SHOW_WITH_ASSIST;
214        }
215        if (Settings.Secure.getIntForUser(mContext.getContentResolver(),
216                Settings.Secure.ASSIST_SCREENSHOT_ENABLED, 1, mUser) == 0) {
217            flags |= VoiceInteractionSession.SHOW_WITH_SCREENSHOT;
218        }
219        return flags;
220    }
221
222    public boolean showLocked(Bundle args, int flags, int disabledContext,
223            IVoiceInteractionSessionShowCallback showCallback, IBinder activityToken,
224            List<IBinder> topActivities) {
225        if (mBound) {
226            if (!mFullyBound) {
227                mFullyBound = mContext.bindServiceAsUser(mBindIntent, mFullConnection,
228                        Context.BIND_AUTO_CREATE | Context.BIND_TREAT_LIKE_ACTIVITY
229                                | Context.BIND_FOREGROUND_SERVICE,
230                        new UserHandle(mUser));
231            }
232            mShown = true;
233            boolean isAssistDataAllowed = true;
234            try {
235                isAssistDataAllowed = mAm.isAssistDataAllowedOnCurrentActivity();
236            } catch (RemoteException e) {
237            }
238            disabledContext |= getUserDisabledShowContextLocked();
239            boolean structureEnabled = isAssistDataAllowed
240                    && (disabledContext&VoiceInteractionSession.SHOW_WITH_ASSIST) == 0;
241            boolean screenshotEnabled = isAssistDataAllowed && structureEnabled
242                    && (disabledContext&VoiceInteractionSession.SHOW_WITH_SCREENSHOT) == 0;
243            mShowArgs = args;
244            mShowFlags = flags;
245            mHaveAssistData = false;
246            mPendingAssistDataCount = 0;
247            boolean needDisclosure = false;
248            if ((flags&VoiceInteractionSession.SHOW_WITH_ASSIST) != 0) {
249                if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ASSIST_STRUCTURE, mCallingUid,
250                        mSessionComponentName.getPackageName()) == AppOpsManager.MODE_ALLOWED
251                        && structureEnabled) {
252                    mAssistData.clear();
253                    final int count = activityToken != null ? 1 : topActivities.size();
254                    // Temp workaround for bug: 28348867  Revert after DP3
255                    for (int i = 0; i < count && i < 1; i++) {
256                        IBinder topActivity = count == 1 ? activityToken : topActivities.get(i);
257                        try {
258                            MetricsLogger.count(mContext, "assist_with_context", 1);
259                            Bundle receiverExtras = new Bundle();
260                            receiverExtras.putInt(KEY_RECEIVER_EXTRA_INDEX, i);
261                            receiverExtras.putInt(KEY_RECEIVER_EXTRA_COUNT, count);
262                            if (mAm.requestAssistContextExtras(ActivityManager.ASSIST_CONTEXT_FULL,
263                                    mAssistReceiver, receiverExtras, topActivity,
264                                    /* focused= */ i == 0, /* newSessionId= */ i == 0)) {
265                                needDisclosure = true;
266                                mPendingAssistDataCount++;
267                            } else if (i == 0) {
268                                // Wasn't allowed... given that, let's not do the screenshot either.
269                                mHaveAssistData = true;
270                                mAssistData.clear();
271                                screenshotEnabled = false;
272                                break;
273                            }
274                        } catch (RemoteException e) {
275                        }
276                    }
277                } else {
278                    mHaveAssistData = true;
279                    mAssistData.clear();
280                }
281            } else {
282                mAssistData.clear();
283            }
284            mHaveScreenshot = false;
285            if ((flags&VoiceInteractionSession.SHOW_WITH_SCREENSHOT) != 0) {
286                if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ASSIST_SCREENSHOT, mCallingUid,
287                        mSessionComponentName.getPackageName()) == AppOpsManager.MODE_ALLOWED
288                        && screenshotEnabled) {
289                    try {
290                        MetricsLogger.count(mContext, "assist_with_screen", 1);
291                        needDisclosure = true;
292                        mIWindowManager.requestAssistScreenshot(mScreenshotReceiver);
293                    } catch (RemoteException e) {
294                    }
295                } else {
296                    mHaveScreenshot = true;
297                    mScreenshot = null;
298                }
299            } else {
300                mScreenshot = null;
301            }
302            if (needDisclosure) {
303                mHandler.post(mShowAssistDisclosureRunnable);
304            }
305            if (mSession != null) {
306                try {
307                    mSession.show(mShowArgs, mShowFlags, showCallback);
308                    mShowArgs = null;
309                    mShowFlags = 0;
310                } catch (RemoteException e) {
311                }
312                deliverSessionDataLocked();
313            } else if (showCallback != null) {
314                mPendingShowCallbacks.add(showCallback);
315            }
316            return true;
317        }
318        if (showCallback != null) {
319            try {
320                showCallback.onFailed();
321            } catch (RemoteException e) {
322            }
323        }
324        return false;
325    }
326
327    void grantUriPermission(Uri uri, int mode, int srcUid, int destUid, String destPkg) {
328        if (!"content".equals(uri.getScheme())) {
329            return;
330        }
331        long ident = Binder.clearCallingIdentity();
332        try {
333            // This will throw SecurityException for us.
334            mAm.checkGrantUriPermission(srcUid, null, ContentProvider.getUriWithoutUserId(uri),
335                    mode, ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(srcUid)));
336            // No security exception, do the grant.
337            int sourceUserId = ContentProvider.getUserIdFromUri(uri, mUser);
338            uri = ContentProvider.getUriWithoutUserId(uri);
339            mAm.grantUriPermissionFromOwner(mPermissionOwner, srcUid, destPkg,
340                    uri, Intent.FLAG_GRANT_READ_URI_PERMISSION, sourceUserId, mUser);
341        } catch (RemoteException e) {
342        } catch (SecurityException e) {
343            Slog.w(TAG, "Can't propagate permission", e);
344        } finally {
345            Binder.restoreCallingIdentity(ident);
346        }
347
348    }
349
350    void grantClipDataItemPermission(ClipData.Item item, int mode, int srcUid, int destUid,
351            String destPkg) {
352        if (item.getUri() != null) {
353            grantUriPermission(item.getUri(), mode, srcUid, destUid, destPkg);
354        }
355        Intent intent = item.getIntent();
356        if (intent != null && intent.getData() != null) {
357            grantUriPermission(intent.getData(), mode, srcUid, destUid, destPkg);
358        }
359    }
360
361    void grantClipDataPermissions(ClipData data, int mode, int srcUid, int destUid,
362            String destPkg) {
363        final int N = data.getItemCount();
364        for (int i=0; i<N; i++) {
365            grantClipDataItemPermission(data.getItemAt(i), mode, srcUid, destUid, destPkg);
366        }
367    }
368
369    void deliverSessionDataLocked() {
370        if (mSession == null) {
371            return;
372        }
373        if (mHaveAssistData) {
374            AssistDataForActivity assistData;
375            if (mAssistData.isEmpty()) {
376                // We're not actually going to get any data, deliver some nothing
377                try {
378                    mSession.handleAssist(null, null, null, 0, 0);
379                } catch (RemoteException e) {
380                }
381            } else {
382                while (!mAssistData.isEmpty()) {
383                    if (mPendingAssistDataCount <= 0) {
384                        Slog.e(TAG, "mPendingAssistDataCount is " + mPendingAssistDataCount);
385                    }
386                    mPendingAssistDataCount--;
387                    assistData = mAssistData.remove(0);
388                    if (assistData.data == null) {
389                        try {
390                            mSession.handleAssist(null, null, null, assistData.activityIndex,
391                                    assistData.activityCount);
392                        } catch (RemoteException e) {
393                        }
394                    } else {
395                        deliverSessionDataLocked(assistData);
396                    }
397                }
398            }
399            if (mPendingAssistDataCount <= 0) {
400                mHaveAssistData = false;
401            } // else, more to come
402        }
403        if (mHaveScreenshot) {
404            try {
405                mSession.handleScreenshot(mScreenshot);
406            } catch (RemoteException e) {
407            }
408            mScreenshot = null;
409            mHaveScreenshot = false;
410        }
411    }
412
413    private void deliverSessionDataLocked(AssistDataForActivity assistDataForActivity) {
414        Bundle assistData = assistDataForActivity.data.getBundle(
415                VoiceInteractionSession.KEY_DATA);
416        AssistStructure structure = assistDataForActivity.data.getParcelable(
417                VoiceInteractionSession.KEY_STRUCTURE);
418        AssistContent content = assistDataForActivity.data.getParcelable(
419                VoiceInteractionSession.KEY_CONTENT);
420        int uid = assistDataForActivity.data.getInt(Intent.EXTRA_ASSIST_UID, -1);
421        if (uid >= 0 && content != null) {
422            Intent intent = content.getIntent();
423            if (intent != null) {
424                ClipData data = intent.getClipData();
425                if (data != null && Intent.isAccessUriMode(intent.getFlags())) {
426                    grantClipDataPermissions(data, intent.getFlags(), uid,
427                            mCallingUid, mSessionComponentName.getPackageName());
428                }
429            }
430            ClipData data = content.getClipData();
431            if (data != null) {
432                grantClipDataPermissions(data,
433                        Intent.FLAG_GRANT_READ_URI_PERMISSION,
434                        uid, mCallingUid, mSessionComponentName.getPackageName());
435            }
436        }
437        try {
438            mSession.handleAssist(assistData, structure, content,
439                    assistDataForActivity.activityIndex, assistDataForActivity.activityCount);
440        } catch (RemoteException e) {
441        }
442    }
443
444    public boolean hideLocked() {
445        if (mBound) {
446            if (mShown) {
447                mShown = false;
448                mShowArgs = null;
449                mShowFlags = 0;
450                mHaveAssistData = false;
451                mAssistData.clear();
452                if (mSession != null) {
453                    try {
454                        mSession.hide();
455                    } catch (RemoteException e) {
456                    }
457                }
458                try {
459                    mAm.revokeUriPermissionFromOwner(mPermissionOwner, null,
460                            Intent.FLAG_GRANT_READ_URI_PERMISSION
461                                    | Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
462                            mUser);
463                } catch (RemoteException e) {
464                }
465                if (mSession != null) {
466                    try {
467                        mAm.finishVoiceTask(mSession);
468                    } catch (RemoteException e) {
469                    }
470                }
471            }
472            if (mFullyBound) {
473                mContext.unbindService(mFullConnection);
474                mFullyBound = false;
475            }
476            return true;
477        }
478        return false;
479    }
480
481    public void cancelLocked(boolean finishTask) {
482        hideLocked();
483        mCanceled = true;
484        if (mBound) {
485            if (mSession != null) {
486                try {
487                    mSession.destroy();
488                } catch (RemoteException e) {
489                    Slog.w(TAG, "Voice interation session already dead");
490                }
491            }
492            if (finishTask && mSession != null) {
493                try {
494                    mAm.finishVoiceTask(mSession);
495                } catch (RemoteException e) {
496                }
497            }
498            mContext.unbindService(this);
499            try {
500                mIWindowManager.removeWindowToken(mToken);
501            } catch (RemoteException e) {
502                Slog.w(TAG, "Failed removing window token", e);
503            }
504            mBound = false;
505            mService = null;
506            mSession = null;
507            mInteractor = null;
508        }
509        if (mFullyBound) {
510            mContext.unbindService(mFullConnection);
511            mFullyBound = false;
512        }
513    }
514
515    public boolean deliverNewSessionLocked(IVoiceInteractionSession session,
516            IVoiceInteractor interactor) {
517        mSession = session;
518        mInteractor = interactor;
519        if (mShown) {
520            try {
521                session.show(mShowArgs, mShowFlags, mShowCallback);
522                mShowArgs = null;
523                mShowFlags = 0;
524            } catch (RemoteException e) {
525            }
526            deliverSessionDataLocked();
527        }
528        return true;
529    }
530
531    private void notifyPendingShowCallbacksShownLocked() {
532        for (int i = 0; i < mPendingShowCallbacks.size(); i++) {
533            try {
534                mPendingShowCallbacks.get(i).onShown();
535            } catch (RemoteException e) {
536            }
537        }
538        mPendingShowCallbacks.clear();
539    }
540
541    private void notifyPendingShowCallbacksFailedLocked() {
542        for (int i = 0; i < mPendingShowCallbacks.size(); i++) {
543            try {
544                mPendingShowCallbacks.get(i).onFailed();
545            } catch (RemoteException e) {
546            }
547        }
548        mPendingShowCallbacks.clear();
549    }
550
551    @Override
552    public void onServiceConnected(ComponentName name, IBinder service) {
553        synchronized (mLock) {
554            mService = IVoiceInteractionSessionService.Stub.asInterface(service);
555            if (!mCanceled) {
556                try {
557                    mService.newSession(mToken, mShowArgs, mShowFlags);
558                } catch (RemoteException e) {
559                    Slog.w(TAG, "Failed adding window token", e);
560                }
561            }
562        }
563    }
564
565    @Override
566    public void onServiceDisconnected(ComponentName name) {
567        mCallback.sessionConnectionGone(this);
568        mService = null;
569    }
570
571    public void dump(String prefix, PrintWriter pw) {
572        pw.print(prefix); pw.print("mToken="); pw.println(mToken);
573        pw.print(prefix); pw.print("mShown="); pw.println(mShown);
574        pw.print(prefix); pw.print("mShowArgs="); pw.println(mShowArgs);
575        pw.print(prefix); pw.print("mShowFlags=0x"); pw.println(Integer.toHexString(mShowFlags));
576        pw.print(prefix); pw.print("mBound="); pw.println(mBound);
577        if (mBound) {
578            pw.print(prefix); pw.print("mService="); pw.println(mService);
579            pw.print(prefix); pw.print("mSession="); pw.println(mSession);
580            pw.print(prefix); pw.print("mInteractor="); pw.println(mInteractor);
581        }
582        pw.print(prefix); pw.print("mHaveAssistData="); pw.println(mHaveAssistData);
583        if (mHaveAssistData) {
584            pw.print(prefix); pw.print("mAssistData="); pw.println(mAssistData);
585        }
586    }
587
588    private Runnable mShowAssistDisclosureRunnable = new Runnable() {
589        @Override
590        public void run() {
591            StatusBarManagerInternal statusBarInternal = LocalServices.getService(
592                    StatusBarManagerInternal.class);
593            if (statusBarInternal != null) {
594                statusBarInternal.showAssistDisclosure();
595            }
596        }
597    };
598};
599