OSDN Git Service

DO NOT MERGE. Grant MMS Uri permissions as the calling UID.
[android-x86/frameworks-base.git] / services / voiceinteraction / java / com / android / server / voiceinteraction / VoiceInteractionManagerServiceImpl.java
1 /*
2  * Copyright (C) 2014 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 android.app.ActivityManager;
20 import android.app.ActivityManagerInternal;
21 import android.app.ActivityManagerNative;
22 import android.app.IActivityManager;
23 import android.content.BroadcastReceiver;
24 import android.content.ComponentName;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.IntentFilter;
28 import android.content.ServiceConnection;
29 import android.content.pm.PackageManager;
30 import android.os.Bundle;
31 import android.os.Handler;
32 import android.os.IBinder;
33 import android.os.RemoteException;
34 import android.os.ServiceManager;
35 import android.os.UserHandle;
36 import android.service.voice.IVoiceInteractionService;
37 import android.service.voice.IVoiceInteractionSession;
38 import android.service.voice.VoiceInteractionService;
39 import android.service.voice.VoiceInteractionServiceInfo;
40 import android.util.PrintWriterPrinter;
41 import android.util.Slog;
42 import android.view.IWindowManager;
43
44 import com.android.internal.app.IVoiceInteractionSessionListener;
45 import com.android.internal.app.IVoiceInteractionSessionShowCallback;
46 import com.android.internal.app.IVoiceInteractor;
47 import com.android.server.LocalServices;
48
49 import java.io.FileDescriptor;
50 import java.io.PrintWriter;
51 import java.util.List;
52
53 class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConnection.Callback {
54     final static String TAG = "VoiceInteractionServiceManager";
55
56     final static String CLOSE_REASON_VOICE_INTERACTION = "voiceinteraction";
57
58     final boolean mValid;
59
60     final Context mContext;
61     final Handler mHandler;
62     final VoiceInteractionManagerService.VoiceInteractionManagerServiceStub mServiceStub;
63     final int mUser;
64     final ComponentName mComponent;
65     final IActivityManager mAm;
66     final VoiceInteractionServiceInfo mInfo;
67     final ComponentName mSessionComponentName;
68     final IWindowManager mIWindowManager;
69     boolean mBound = false;
70     IVoiceInteractionService mService;
71
72     VoiceInteractionSessionConnection mActiveSession;
73     int mDisabledShowContext;
74
75     final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
76         @Override
77         public void onReceive(Context context, Intent intent) {
78             if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
79                 String reason = intent.getStringExtra("reason");
80                 if (!CLOSE_REASON_VOICE_INTERACTION.equals(reason) && !"dream".equals(reason)) {
81                     synchronized (mServiceStub) {
82                         if (mActiveSession != null && mActiveSession.mSession != null) {
83                             try {
84                                 mActiveSession.mSession.closeSystemDialogs();
85                             } catch (RemoteException e) {
86                             }
87                         }
88                     }
89                 }
90             }
91         }
92     };
93
94     final ServiceConnection mConnection = new ServiceConnection() {
95         @Override
96         public void onServiceConnected(ComponentName name, IBinder service) {
97             synchronized (mServiceStub) {
98                 mService = IVoiceInteractionService.Stub.asInterface(service);
99                 try {
100                     mService.ready();
101                 } catch (RemoteException e) {
102                 }
103             }
104         }
105
106         @Override
107         public void onServiceDisconnected(ComponentName name) {
108             mService = null;
109         }
110     };
111
112     VoiceInteractionManagerServiceImpl(Context context, Handler handler,
113             VoiceInteractionManagerService.VoiceInteractionManagerServiceStub stub,
114             int userHandle, ComponentName service) {
115         mContext = context;
116         mHandler = handler;
117         mServiceStub = stub;
118         mUser = userHandle;
119         mComponent = service;
120         mAm = ActivityManagerNative.getDefault();
121         VoiceInteractionServiceInfo info;
122         try {
123             info = new VoiceInteractionServiceInfo(context.getPackageManager(), service, mUser);
124         } catch (PackageManager.NameNotFoundException e) {
125             Slog.w(TAG, "Voice interaction service not found: " + service, e);
126             mInfo = null;
127             mSessionComponentName = null;
128             mIWindowManager = null;
129             mValid = false;
130             return;
131         }
132         mInfo = info;
133         if (mInfo.getParseError() != null) {
134             Slog.w(TAG, "Bad voice interaction service: " + mInfo.getParseError());
135             mSessionComponentName = null;
136             mIWindowManager = null;
137             mValid = false;
138             return;
139         }
140         mValid = true;
141         mSessionComponentName = new ComponentName(service.getPackageName(),
142                 mInfo.getSessionService());
143         mIWindowManager = IWindowManager.Stub.asInterface(
144                 ServiceManager.getService(Context.WINDOW_SERVICE));
145         IntentFilter filter = new IntentFilter();
146         filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
147         mContext.registerReceiver(mBroadcastReceiver, filter, null, handler);
148     }
149
150     public boolean showSessionLocked(Bundle args, int flags,
151             IVoiceInteractionSessionShowCallback showCallback, IBinder activityToken) {
152         if (mActiveSession == null) {
153             mActiveSession = new VoiceInteractionSessionConnection(mServiceStub,
154                     mSessionComponentName, mUser, mContext, this,
155                     mInfo.getServiceInfo().applicationInfo.uid, mHandler);
156         }
157         List<IBinder> activityTokens = null;
158         if (activityToken == null) {
159             // Let's get top activities from all visible stacks
160             activityTokens = LocalServices.getService(ActivityManagerInternal.class)
161                     .getTopVisibleActivities();
162         }
163         return mActiveSession.showLocked(args, flags, mDisabledShowContext, showCallback,
164                 activityToken, activityTokens);
165     }
166
167     public boolean hideSessionLocked() {
168         if (mActiveSession != null) {
169             return mActiveSession.hideLocked();
170         }
171         return false;
172     }
173
174     public boolean deliverNewSessionLocked(IBinder token,
175             IVoiceInteractionSession session, IVoiceInteractor interactor) {
176         if (mActiveSession == null || token != mActiveSession.mToken) {
177             Slog.w(TAG, "deliverNewSession does not match active session");
178             return false;
179         }
180         mActiveSession.deliverNewSessionLocked(session, interactor);
181         return true;
182     }
183
184     public int startVoiceActivityLocked(int callingPid, int callingUid, IBinder token,
185             Intent intent, String resolvedType) {
186         try {
187             if (mActiveSession == null || token != mActiveSession.mToken) {
188                 Slog.w(TAG, "startVoiceActivity does not match active session");
189                 return ActivityManager.START_VOICE_NOT_ACTIVE_SESSION;
190             }
191             if (!mActiveSession.mShown) {
192                 Slog.w(TAG, "startVoiceActivity not allowed on hidden session");
193                 return ActivityManager.START_VOICE_HIDDEN_SESSION;
194             }
195             intent = new Intent(intent);
196             intent.addCategory(Intent.CATEGORY_VOICE);
197             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
198             return mAm.startVoiceActivity(mComponent.getPackageName(), callingPid, callingUid,
199                     intent, resolvedType, mActiveSession.mSession, mActiveSession.mInteractor,
200                     0, null, null, mUser);
201         } catch (RemoteException e) {
202             throw new IllegalStateException("Unexpected remote error", e);
203         }
204     }
205
206     public void setKeepAwakeLocked(IBinder token, boolean keepAwake) {
207         try {
208             if (mActiveSession == null || token != mActiveSession.mToken) {
209                 Slog.w(TAG, "setKeepAwake does not match active session");
210                 return;
211             }
212             mAm.setVoiceKeepAwake(mActiveSession.mSession, keepAwake);
213         } catch (RemoteException e) {
214             throw new IllegalStateException("Unexpected remote error", e);
215         }
216     }
217
218     public void closeSystemDialogsLocked(IBinder token) {
219         try {
220             if (mActiveSession == null || token != mActiveSession.mToken) {
221                 Slog.w(TAG, "closeSystemDialogs does not match active session");
222                 return;
223             }
224             mAm.closeSystemDialogs(CLOSE_REASON_VOICE_INTERACTION);
225         } catch (RemoteException e) {
226             throw new IllegalStateException("Unexpected remote error", e);
227         }
228     }
229
230     public void finishLocked(IBinder token, boolean finishTask) {
231         if (mActiveSession == null || (!finishTask && token != mActiveSession.mToken)) {
232             Slog.w(TAG, "finish does not match active session");
233             return;
234         }
235         mActiveSession.cancelLocked(finishTask);
236         mActiveSession = null;
237     }
238
239     public void setDisabledShowContextLocked(int callingUid, int flags) {
240         int activeUid = mInfo.getServiceInfo().applicationInfo.uid;
241         if (callingUid != activeUid) {
242             throw new SecurityException("Calling uid " + callingUid
243                     + " does not match active uid " + activeUid);
244         }
245         mDisabledShowContext = flags;
246     }
247
248     public int getDisabledShowContextLocked(int callingUid) {
249         int activeUid = mInfo.getServiceInfo().applicationInfo.uid;
250         if (callingUid != activeUid) {
251             throw new SecurityException("Calling uid " + callingUid
252                     + " does not match active uid " + activeUid);
253         }
254         return mDisabledShowContext;
255     }
256
257     public int getUserDisabledShowContextLocked(int callingUid) {
258         int activeUid = mInfo.getServiceInfo().applicationInfo.uid;
259         if (callingUid != activeUid) {
260             throw new SecurityException("Calling uid " + callingUid
261                     + " does not match active uid " + activeUid);
262         }
263         return mActiveSession != null ? mActiveSession.getUserDisabledShowContextLocked() : 0;
264     }
265
266     public boolean supportsLocalVoiceInteraction() {
267         return mInfo.getSupportsLocalInteraction();
268     }
269
270     public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args) {
271         if (!mValid) {
272             pw.print("  NOT VALID: ");
273             if (mInfo == null) {
274                 pw.println("no info");
275             } else {
276                 pw.println(mInfo.getParseError());
277             }
278             return;
279         }
280         pw.print("  mUser="); pw.println(mUser);
281         pw.print("  mComponent="); pw.println(mComponent.flattenToShortString());
282         pw.print("  Session service="); pw.println(mInfo.getSessionService());
283         pw.println("  Service info:");
284         mInfo.getServiceInfo().dump(new PrintWriterPrinter(pw), "    ");
285         pw.print("  Recognition service="); pw.println(mInfo.getRecognitionService());
286         pw.print("  Settings activity="); pw.println(mInfo.getSettingsActivity());
287         pw.print("  Supports assist="); pw.println(mInfo.getSupportsAssist());
288         pw.print("  Supports launch from keyguard=");
289         pw.println(mInfo.getSupportsLaunchFromKeyguard());
290         if (mDisabledShowContext != 0) {
291             pw.print("  mDisabledShowContext=");
292             pw.println(Integer.toHexString(mDisabledShowContext));
293         }
294         pw.print("  mBound="); pw.print(mBound);  pw.print(" mService="); pw.println(mService);
295         if (mActiveSession != null) {
296             pw.println("  Active session:");
297             mActiveSession.dump("    ", pw);
298         }
299     }
300
301     void startLocked() {
302         Intent intent = new Intent(VoiceInteractionService.SERVICE_INTERFACE);
303         intent.setComponent(mComponent);
304         mBound = mContext.bindServiceAsUser(intent, mConnection,
305                 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, new UserHandle(mUser));
306         if (!mBound) {
307             Slog.w(TAG, "Failed binding to voice interaction service " + mComponent);
308         }
309     }
310
311     public void launchVoiceAssistFromKeyguard() {
312         if (mService == null) {
313             Slog.w(TAG, "Not bound to voice interaction service " + mComponent);
314             return;
315         }
316         try {
317             mService.launchVoiceAssistFromKeyguard();
318         } catch (RemoteException e) {
319             Slog.w(TAG, "RemoteException while calling launchVoiceAssistFromKeyguard", e);
320         }
321     }
322
323     void shutdownLocked() {
324         // If there is an active session, cancel it to allow it to clean up its window and other
325         // state.
326         if (mActiveSession != null) {
327             mActiveSession.cancelLocked(false);
328             mActiveSession = null;
329         }
330         try {
331             if (mService != null) {
332                 mService.shutdown();
333             }
334         } catch (RemoteException e) {
335             Slog.w(TAG, "RemoteException in shutdown", e);
336         }
337
338         if (mBound) {
339             mContext.unbindService(mConnection);
340             mBound = false;
341         }
342         if (mValid) {
343             mContext.unregisterReceiver(mBroadcastReceiver);
344         }
345     }
346
347     void notifySoundModelsChangedLocked() {
348         if (mService == null) {
349             Slog.w(TAG, "Not bound to voice interaction service " + mComponent);
350             return;
351         }
352         try {
353             mService.soundModelsChanged();
354         } catch (RemoteException e) {
355             Slog.w(TAG, "RemoteException while calling soundModelsChanged", e);
356         }
357     }
358
359     @Override
360     public void sessionConnectionGone(VoiceInteractionSessionConnection connection) {
361         synchronized (mServiceStub) {
362             finishLocked(connection.mToken, false);
363         }
364     }
365
366     @Override
367     public void onSessionShown(VoiceInteractionSessionConnection connection) {
368         mServiceStub.onSessionShown();
369     }
370
371     @Override
372     public void onSessionHidden(VoiceInteractionSessionConnection connection) {
373         mServiceStub.onSessionHidden();
374     }
375 }