VoiceInteractionSessionConnection.java revision a83ce1dd2ad3a6b71e90ff4845afc1299fe17b9d
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
17package com.android.server.voiceinteraction;
18
19import android.app.ActivityManager;
20import android.app.ActivityManagerNative;
21import android.app.AssistContent;
22import android.app.IActivityManager;
23import android.content.ClipData;
24import android.content.ComponentName;
25import android.content.ContentProvider;
26import android.content.Context;
27import android.content.Intent;
28import android.content.ServiceConnection;
29import android.net.Uri;
30import android.os.Binder;
31import android.os.Bundle;
32import android.os.IBinder;
33import android.os.RemoteException;
34import android.os.ServiceManager;
35import android.os.UserHandle;
36import android.service.voice.IVoiceInteractionSession;
37import android.service.voice.IVoiceInteractionSessionService;
38import android.service.voice.VoiceInteractionService;
39import android.util.Slog;
40import android.view.IWindowManager;
41import android.view.WindowManager;
42import com.android.internal.app.IVoiceInteractor;
43import com.android.internal.os.IResultReceiver;
44
45import java.io.PrintWriter;
46
47final class VoiceInteractionSessionConnection implements ServiceConnection {
48    final static String TAG = "VoiceInteractionServiceManager";
49
50    final IBinder mToken = new Binder();
51    final Object mLock;
52    final ComponentName mSessionComponentName;
53    final Intent mBindIntent;
54    final int mUser;
55    final Context mContext;
56    final Callback mCallback;
57    final int mCallingPid;
58    final int mCallingUid;
59    final IActivityManager mAm;
60    final IWindowManager mIWindowManager;
61    final IBinder mPermissionOwner;
62    boolean mShown;
63    Bundle mShowArgs;
64    int mShowFlags;
65    boolean mBound;
66    boolean mFullyBound;
67    boolean mCanceled;
68    IVoiceInteractionSessionService mService;
69    IVoiceInteractionSession mSession;
70    IVoiceInteractor mInteractor;
71    boolean mHaveAssistData;
72    Bundle mAssistData;
73
74    public interface Callback {
75        public void sessionConnectionGone(VoiceInteractionSessionConnection connection);
76    }
77
78    final ServiceConnection mFullConnection = new ServiceConnection() {
79        @Override
80        public void onServiceConnected(ComponentName name, IBinder service) {
81        }
82        @Override
83        public void onServiceDisconnected(ComponentName name) {
84        }
85    };
86
87    final IResultReceiver mAssistReceiver = new IResultReceiver.Stub() {
88        @Override
89        public void send(int resultCode, Bundle resultData) throws RemoteException {
90            synchronized (mLock) {
91                if (mShown) {
92                    mHaveAssistData = true;
93                    mAssistData = resultData;
94                    deliverAssistData();
95                }
96            }
97        }
98    };
99
100    public VoiceInteractionSessionConnection(Object lock, ComponentName component, int user,
101            Context context, Callback callback, int callingPid, int callingUid) {
102        mLock = lock;
103        mSessionComponentName = component;
104        mUser = user;
105        mContext = context;
106        mCallback = callback;
107        mCallingPid = callingPid;
108        mCallingUid = callingUid;
109        mAm = ActivityManagerNative.getDefault();
110        mIWindowManager = IWindowManager.Stub.asInterface(
111                ServiceManager.getService(Context.WINDOW_SERVICE));
112        IBinder permOwner = null;
113        try {
114            permOwner = mAm.newUriPermissionOwner("voicesession:"
115                    + component.flattenToShortString());
116        } catch (RemoteException e) {
117            Slog.w("voicesession", "AM dead", e);
118        }
119        mPermissionOwner = permOwner;
120        mBindIntent = new Intent(VoiceInteractionService.SERVICE_INTERFACE);
121        mBindIntent.setComponent(mSessionComponentName);
122        mBound = mContext.bindServiceAsUser(mBindIntent, this,
123                Context.BIND_AUTO_CREATE|Context.BIND_ALLOW_OOM_MANAGEMENT, new UserHandle(mUser));
124        if (mBound) {
125            try {
126                mIWindowManager.addWindowToken(mToken,
127                        WindowManager.LayoutParams.TYPE_VOICE_INTERACTION);
128            } catch (RemoteException e) {
129                Slog.w(TAG, "Failed adding window token", e);
130            }
131        } else {
132            Slog.w(TAG, "Failed binding to voice interaction session service "
133                    + mSessionComponentName);
134        }
135    }
136
137    public boolean showLocked(Bundle args, int flags) {
138        if (mBound) {
139            if (!mFullyBound) {
140                mFullyBound = mContext.bindServiceAsUser(mBindIntent, mFullConnection,
141                        Context.BIND_AUTO_CREATE|Context.BIND_TREAT_LIKE_ACTIVITY,
142                        new UserHandle(mUser));
143            }
144            mShown = true;
145            mShowArgs = args;
146            mShowFlags = flags;
147            if ((flags&VoiceInteractionService.START_WITH_ASSIST) != 0) {
148                try {
149                    mAm.requestAssistContextExtras(ActivityManager.ASSIST_CONTEXT_FULL,
150                            mAssistReceiver);
151                } catch (RemoteException e) {
152                }
153            } else {
154                mHaveAssistData = false;
155                mAssistData = null;
156            }
157            if (mSession != null) {
158                try {
159                    mSession.show(mShowArgs, mShowFlags);
160                    mShowArgs = null;
161                    mShowFlags = 0;
162                } catch (RemoteException e) {
163                }
164                deliverAssistData();
165            }
166            return true;
167        }
168        return false;
169    }
170
171    void grantUriPermission(Uri uri, int mode, int srcUid, int destUid, String destPkg) {
172        if (!"content".equals(uri.getScheme())) {
173            return;
174        }
175        long ident = Binder.clearCallingIdentity();
176        try {
177            // This will throw SecurityException for us.
178            mAm.checkGrantUriPermission(srcUid, null, ContentProvider.getUriWithoutUserId(uri),
179                    mode, ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(srcUid)));
180            // No security exception, do the grant.
181            int sourceUserId = ContentProvider.getUserIdFromUri(uri, mUser);
182            uri = ContentProvider.getUriWithoutUserId(uri);
183            mAm.grantUriPermissionFromOwner(mPermissionOwner, srcUid, destPkg,
184                    uri, Intent.FLAG_GRANT_READ_URI_PERMISSION, sourceUserId, mUser);
185        } catch (RemoteException e) {
186        } catch (SecurityException e) {
187            Slog.w(TAG, "Can't propagate permission", e);
188        } finally {
189            Binder.restoreCallingIdentity(ident);
190        }
191
192    }
193
194    void grantClipDataItemPermission(ClipData.Item item, int mode, int srcUid, int destUid,
195            String destPkg) {
196        if (item.getUri() != null) {
197            grantUriPermission(item.getUri(), mode, srcUid, destUid, destPkg);
198        }
199        Intent intent = item.getIntent();
200        if (intent != null && intent.getData() != null) {
201            grantUriPermission(intent.getData(), mode, srcUid, destUid, destPkg);
202        }
203    }
204
205    void grantClipDataPermissions(ClipData data, int mode, int srcUid, int destUid,
206            String destPkg) {
207        final int N = data.getItemCount();
208        for (int i=0; i<N; i++) {
209            grantClipDataItemPermission(data.getItemAt(i), mode, srcUid, destUid, destPkg);
210        }
211    }
212
213    void deliverAssistData() {
214        if (mSession == null || !mHaveAssistData) {
215            return;
216        }
217        if (mAssistData != null) {
218            int uid = mAssistData.getInt(Intent.EXTRA_ASSIST_UID, -1);
219            if (uid >= 0) {
220                Bundle assistContext = mAssistData.getBundle(Intent.EXTRA_ASSIST_CONTEXT);
221                if (assistContext != null) {
222                    AssistContent content = AssistContent.getAssistContent(assistContext);
223                    if (content != null) {
224                        Intent intent = content.getIntent();
225                        if (intent != null) {
226                            ClipData data = intent.getClipData();
227                            if (data != null && Intent.isAccessUriMode(intent.getFlags())) {
228                                grantClipDataPermissions(data, intent.getFlags(), uid,
229                                        mCallingUid, mSessionComponentName.getPackageName());
230                            }
231                        }
232                        ClipData data = content.getClipData();
233                        if (data != null) {
234                            grantClipDataPermissions(data, Intent.FLAG_GRANT_READ_URI_PERMISSION,
235                                    uid, mCallingUid, mSessionComponentName.getPackageName());
236                        }
237                    }
238                }
239            }
240        }
241        try {
242            mSession.handleAssist(mAssistData);
243            mAssistData = null;
244            mHaveAssistData = false;
245        } catch (RemoteException e) {
246        }
247    }
248
249    public boolean hideLocked() {
250        if (mBound) {
251            if (mShown) {
252                mShown = false;
253                mShowArgs = null;
254                mShowFlags = 0;
255                mHaveAssistData = false;
256                mAssistData = null;
257                if (mSession != null) {
258                    try {
259                        mSession.hide();
260                    } catch (RemoteException e) {
261                    }
262                }
263                try {
264                    mAm.revokeUriPermissionFromOwner(mPermissionOwner, null,
265                            Intent.FLAG_GRANT_READ_URI_PERMISSION
266                                    | Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
267                            mUser);
268                } catch (RemoteException e) {
269                }
270            }
271            if (mFullyBound) {
272                mContext.unbindService(mFullConnection);
273                mFullyBound = false;
274            }
275            return true;
276        }
277        return false;
278    }
279
280    public boolean deliverNewSessionLocked(IVoiceInteractionSession session,
281            IVoiceInteractor interactor) {
282        mSession = session;
283        mInteractor = interactor;
284        if (mShown) {
285            try {
286                session.show(mShowArgs, mShowFlags);
287                mShowArgs = null;
288                mShowFlags = 0;
289            } catch (RemoteException e) {
290            }
291            if (mHaveAssistData) {
292                try {
293                    session.handleAssist(mAssistData);
294                    mAssistData = null;
295                    mHaveAssistData = false;
296                } catch (RemoteException e) {
297                }
298            }
299        }
300        return true;
301    }
302
303    @Override
304    public void onServiceConnected(ComponentName name, IBinder service) {
305        synchronized (mLock) {
306            mService = IVoiceInteractionSessionService.Stub.asInterface(service);
307            if (!mCanceled) {
308                try {
309                    mService.newSession(mToken, mShowArgs, mShowFlags);
310                } catch (RemoteException e) {
311                    Slog.w(TAG, "Failed adding window token", e);
312                }
313            }
314        }
315    }
316
317    @Override
318    public void onServiceDisconnected(ComponentName name) {
319        mCallback.sessionConnectionGone(this);
320        mService = null;
321    }
322
323    public void cancel() {
324        mCanceled = true;
325        if (mBound) {
326            if (mSession != null) {
327                try {
328                    mSession.destroy();
329                } catch (RemoteException e) {
330                    Slog.w(TAG, "Voice interation session already dead");
331                }
332            }
333            if (mSession != null) {
334                try {
335                    mAm.finishVoiceTask(mSession);
336                } catch (RemoteException e) {
337                }
338            }
339            mContext.unbindService(this);
340            try {
341                mIWindowManager.removeWindowToken(mToken);
342            } catch (RemoteException e) {
343                Slog.w(TAG, "Failed removing window token", e);
344            }
345            mBound = false;
346            mService = null;
347            mSession = null;
348            mInteractor = null;
349        }
350        if (mFullyBound) {
351            mContext.unbindService(mFullConnection);
352            mFullyBound = false;
353        }
354    }
355
356    public void dump(String prefix, PrintWriter pw) {
357        pw.print(prefix); pw.print("mToken="); pw.println(mToken);
358        pw.print(prefix); pw.print("mShown="); pw.println(mShown);
359        pw.print(prefix); pw.print("mShowArgs="); pw.println(mShowArgs);
360        pw.print(prefix); pw.print("mShowFlags=0x"); pw.println(Integer.toHexString(mShowFlags));
361        pw.print(prefix); pw.print("mBound="); pw.println(mBound);
362        if (mBound) {
363            pw.print(prefix); pw.print("mService="); pw.println(mService);
364            pw.print(prefix); pw.print("mSession="); pw.println(mSession);
365            pw.print(prefix); pw.print("mInteractor="); pw.println(mInteractor);
366        }
367        pw.print(prefix); pw.print("mHaveAssistData="); pw.println(mHaveAssistData);
368        if (mHaveAssistData) {
369            pw.print(prefix); pw.print("mAssistData="); pw.println(mAssistData);
370        }
371    }
372};
373