VoiceInteractionManagerServiceImpl.java revision 6daae9622672e0b38fc2efed29f68061d749cacc
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
17package com.android.server.voiceinteraction;
18
19import android.app.ActivityManager;
20import android.app.ActivityManagerNative;
21import android.app.IActivityManager;
22import android.content.BroadcastReceiver;
23import android.content.ComponentName;
24import android.content.Context;
25import android.content.Intent;
26import android.content.IntentFilter;
27import android.content.ServiceConnection;
28import android.content.pm.PackageManager;
29import android.os.Binder;
30import android.os.Bundle;
31import android.os.Handler;
32import android.os.IBinder;
33import android.os.RemoteException;
34import android.os.ServiceManager;
35import android.os.UserHandle;
36import android.service.voice.IVoiceInteractionService;
37import android.service.voice.IVoiceInteractionSession;
38import android.service.voice.IVoiceInteractionSessionService;
39import android.service.voice.VoiceInteractionService;
40import android.service.voice.VoiceInteractionServiceInfo;
41import android.util.Slog;
42import android.view.IWindowManager;
43import android.view.WindowManager;
44
45import com.android.internal.app.IVoiceInteractor;
46
47import java.io.FileDescriptor;
48import java.io.PrintWriter;
49
50class VoiceInteractionManagerServiceImpl {
51    final static String TAG = "VoiceInteractionServiceManager";
52
53    final boolean mValid;
54
55    final Context mContext;
56    final Handler mHandler;
57    final Object mLock;
58    final int mUser;
59    final ComponentName mComponent;
60    final IActivityManager mAm;
61    final VoiceInteractionServiceInfo mInfo;
62    final ComponentName mSessionComponentName;
63    final IWindowManager mIWindowManager;
64    boolean mBound = false;
65    IVoiceInteractionService mService;
66
67    SessionConnection mActiveSession;
68
69    final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
70        @Override
71        public void onReceive(Context context, Intent intent) {
72            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
73                synchronized (mLock) {
74                    if (mActiveSession != null && mActiveSession.mSession != null) {
75                        try {
76                            mActiveSession.mSession.closeSystemDialogs();
77                        } catch (RemoteException e) {
78                        }
79                    }
80                }
81            }
82        }
83    };
84
85    final ServiceConnection mConnection = new ServiceConnection() {
86        @Override
87        public void onServiceConnected(ComponentName name, IBinder service) {
88            synchronized (mLock) {
89                mService = IVoiceInteractionService.Stub.asInterface(service);
90                try {
91                    mService.ready();
92                } catch (RemoteException e) {
93                }
94            }
95        }
96
97        @Override
98        public void onServiceDisconnected(ComponentName name) {
99            try {
100                if (mService != null) {
101                    mService.shutdown();
102                }
103            } catch (RemoteException e) {
104                Slog.w(TAG, "RemoteException in shutdown", e);
105            }
106            mService = null;
107        }
108    };
109
110    final class SessionConnection implements ServiceConnection {
111        final IBinder mToken = new Binder();
112        final Bundle mArgs;
113        boolean mBound;
114        IVoiceInteractionSessionService mService;
115        IVoiceInteractionSession mSession;
116        IVoiceInteractor mInteractor;
117
118        SessionConnection(Bundle args) {
119            mArgs = args;
120            Intent serviceIntent = new Intent(VoiceInteractionService.SERVICE_INTERFACE);
121            serviceIntent.setComponent(mSessionComponentName);
122            mBound = mContext.bindServiceAsUser(serviceIntent, this,
123                    Context.BIND_AUTO_CREATE, 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 " + mComponent);
133            }
134        }
135
136        @Override
137        public void onServiceConnected(ComponentName name, IBinder service) {
138            synchronized (mLock) {
139                mService = IVoiceInteractionSessionService.Stub.asInterface(service);
140                if (mActiveSession == this) {
141                    try {
142                        mService.newSession(mToken, mArgs);
143                    } catch (RemoteException e) {
144                        Slog.w(TAG, "Failed adding window token", e);
145                    }
146                }
147            }
148        }
149
150        @Override
151        public void onServiceDisconnected(ComponentName name) {
152            mService = null;
153        }
154
155        public void cancel() {
156            if (mBound) {
157                if (mSession != null) {
158                    try {
159                        mSession.destroy();
160                    } catch (RemoteException e) {
161                        Slog.w(TAG, "Voice interation session already dead");
162                    }
163                }
164                if (mSession != null) {
165                    try {
166                        mAm.finishVoiceTask(mSession);
167                    } catch (RemoteException e) {
168                    }
169                }
170                mContext.unbindService(this);
171                try {
172                    mIWindowManager.removeWindowToken(mToken);
173                } catch (RemoteException e) {
174                    Slog.w(TAG, "Failed removing window token", e);
175                }
176                mBound = false;
177                mService = null;
178                mSession = null;
179                mInteractor = null;
180            }
181        }
182
183        public void dump(String prefix, PrintWriter pw) {
184            pw.print(prefix); pw.print("mToken="); pw.println(mToken);
185            pw.print(prefix); pw.print("mArgs="); pw.println(mArgs);
186            pw.print(prefix); pw.print("mBound="); pw.println(mBound);
187            if (mBound) {
188                pw.print(prefix); pw.print("mService="); pw.println(mService);
189                pw.print(prefix); pw.print("mSession="); pw.println(mSession);
190                pw.print(prefix); pw.print("mInteractor="); pw.println(mInteractor);
191            }
192        }
193    };
194
195    VoiceInteractionManagerServiceImpl(Context context, Handler handler, Object lock,
196            int userHandle, ComponentName service) {
197        mContext = context;
198        mHandler = handler;
199        mLock = lock;
200        mUser = userHandle;
201        mComponent = service;
202        mAm = ActivityManagerNative.getDefault();
203        VoiceInteractionServiceInfo info;
204        try {
205            info = new VoiceInteractionServiceInfo(context.getPackageManager(), service);
206        } catch (PackageManager.NameNotFoundException e) {
207            Slog.w(TAG, "Voice interaction service not found: " + service);
208            mInfo = null;
209            mSessionComponentName = null;
210            mIWindowManager = null;
211            mValid = false;
212            return;
213        }
214        mInfo = info;
215        if (mInfo.getParseError() != null) {
216            Slog.w(TAG, "Bad voice interaction service: " + mInfo.getParseError());
217            mSessionComponentName = null;
218            mIWindowManager = null;
219            mValid = false;
220            return;
221        }
222        mValid = true;
223        mSessionComponentName = new ComponentName(service.getPackageName(),
224                mInfo.getSessionService());
225        mIWindowManager = IWindowManager.Stub.asInterface(
226                ServiceManager.getService(Context.WINDOW_SERVICE));
227        IntentFilter filter = new IntentFilter();
228        filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
229        mContext.registerReceiver(mBroadcastReceiver, filter, null, handler);
230    }
231
232    public void startSessionLocked(int callingPid, int callingUid, Bundle args) {
233        if (mActiveSession != null) {
234            mActiveSession.cancel();
235            mActiveSession = null;
236        }
237        mActiveSession = new SessionConnection(args);
238    }
239
240    public boolean deliverNewSessionLocked(int callingPid, int callingUid, IBinder token,
241            IVoiceInteractionSession session, IVoiceInteractor interactor) {
242        if (mActiveSession == null || token != mActiveSession.mToken) {
243            Slog.w(TAG, "deliverNewSession does not match active session");
244            return false;
245        }
246        mActiveSession.mSession = session;
247        mActiveSession.mInteractor = interactor;
248        return true;
249    }
250
251    public int startVoiceActivityLocked(int callingPid, int callingUid, IBinder token,
252            Intent intent, String resolvedType) {
253        try {
254            if (mActiveSession == null || token != mActiveSession.mToken) {
255                Slog.w(TAG, "startVoiceActivity does not match active session");
256                return ActivityManager.START_CANCELED;
257            }
258            intent = new Intent(intent);
259            intent.addCategory(Intent.CATEGORY_VOICE);
260            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
261            return mAm.startVoiceActivity(mComponent.getPackageName(), callingPid, callingUid,
262                    intent, resolvedType, mActiveSession.mSession, mActiveSession.mInteractor,
263                    0, null, null, null, mUser);
264        } catch (RemoteException e) {
265            throw new IllegalStateException("Unexpected remote error", e);
266        }
267    }
268
269
270    public void finishLocked(int callingPid, int callingUid, IBinder token) {
271        if (mActiveSession == null || token != mActiveSession.mToken) {
272            Slog.w(TAG, "finish does not match active session");
273            return;
274        }
275        mActiveSession.cancel();
276        mActiveSession = null;
277    }
278
279    public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args) {
280        if (!mValid) {
281            pw.print("  NOT VALID: ");
282            if (mInfo == null) {
283                pw.println("no info");
284            } else {
285                pw.println(mInfo.getParseError());
286            }
287            return;
288        }
289        pw.print("  mComponent="); pw.println(mComponent.flattenToShortString());
290        pw.print("  Session service="); pw.println(mInfo.getSessionService());
291        pw.print("  Settings activity="); pw.println(mInfo.getSettingsActivity());
292        pw.print("  mBound="); pw.print(mBound);  pw.print(" mService="); pw.println(mService);
293        if (mActiveSession != null) {
294            pw.println("  Active session:");
295            mActiveSession.dump("    ", pw);
296        }
297    }
298
299    void startLocked() {
300        Intent intent = new Intent(VoiceInteractionService.SERVICE_INTERFACE);
301        intent.setComponent(mComponent);
302        mBound = mContext.bindServiceAsUser(intent, mConnection,
303                Context.BIND_AUTO_CREATE, new UserHandle(mUser));
304        if (!mBound) {
305            Slog.w(TAG, "Failed binding to voice interaction service " + mComponent);
306        }
307    }
308
309    void shutdownLocked() {
310        if (mBound) {
311            mContext.unbindService(mConnection);
312            mBound = false;
313        }
314        if (mValid) {
315            mContext.unregisterReceiver(mBroadcastReceiver);
316        }
317    }
318
319    void notifySoundModelsChangedLocked() {
320        if (mService == null) {
321            Slog.w(TAG, "Not bound to voice interaction service " + mComponent);
322        }
323        try {
324            mService.soundModelsChanged();
325        } catch (RemoteException e) {
326            Slog.w(TAG, "RemoteException while calling soundModelsChanged", e);
327        }
328    }
329}
330