VoiceInteractionManagerServiceImpl.java revision 69c6adc96eecfde74ceb83cf9177428dc08b6067
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.Bundle;
30import android.os.Handler;
31import android.os.IBinder;
32import android.os.RemoteException;
33import android.os.ServiceManager;
34import android.os.UserHandle;
35import android.service.voice.IVoiceInteractionService;
36import android.service.voice.IVoiceInteractionSession;
37import android.service.voice.VoiceInteractionService;
38import android.service.voice.VoiceInteractionServiceInfo;
39import android.util.Slog;
40import android.view.IWindowManager;
41
42import com.android.internal.app.IVoiceInteractionSessionShowCallback;
43import com.android.internal.app.IVoiceInteractor;
44
45import java.io.FileDescriptor;
46import java.io.PrintWriter;
47
48class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConnection.Callback {
49    final static String TAG = "VoiceInteractionServiceManager";
50
51    final boolean mValid;
52
53    final Context mContext;
54    final Handler mHandler;
55    final Object mLock;
56    final int mUser;
57    final ComponentName mComponent;
58    final IActivityManager mAm;
59    final VoiceInteractionServiceInfo mInfo;
60    final ComponentName mSessionComponentName;
61    final IWindowManager mIWindowManager;
62    boolean mBound = false;
63    IVoiceInteractionService mService;
64
65    VoiceInteractionSessionConnection mActiveSession;
66
67    final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
68        @Override
69        public void onReceive(Context context, Intent intent) {
70            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
71                synchronized (mLock) {
72                    if (mActiveSession != null && mActiveSession.mSession != null) {
73                        try {
74                            mActiveSession.mSession.closeSystemDialogs();
75                        } catch (RemoteException e) {
76                        }
77                    }
78                }
79            }
80        }
81    };
82
83    final ServiceConnection mConnection = new ServiceConnection() {
84        @Override
85        public void onServiceConnected(ComponentName name, IBinder service) {
86            synchronized (mLock) {
87                mService = IVoiceInteractionService.Stub.asInterface(service);
88                try {
89                    mService.ready();
90                } catch (RemoteException e) {
91                }
92            }
93        }
94
95        @Override
96        public void onServiceDisconnected(ComponentName name) {
97            mService = null;
98        }
99    };
100
101    VoiceInteractionManagerServiceImpl(Context context, Handler handler, Object lock,
102            int userHandle, ComponentName service) {
103        mContext = context;
104        mHandler = handler;
105        mLock = lock;
106        mUser = userHandle;
107        mComponent = service;
108        mAm = ActivityManagerNative.getDefault();
109        VoiceInteractionServiceInfo info;
110        try {
111            info = new VoiceInteractionServiceInfo(context.getPackageManager(), service);
112        } catch (PackageManager.NameNotFoundException e) {
113            Slog.w(TAG, "Voice interaction service not found: " + service);
114            mInfo = null;
115            mSessionComponentName = null;
116            mIWindowManager = null;
117            mValid = false;
118            return;
119        }
120        mInfo = info;
121        if (mInfo.getParseError() != null) {
122            Slog.w(TAG, "Bad voice interaction service: " + mInfo.getParseError());
123            mSessionComponentName = null;
124            mIWindowManager = null;
125            mValid = false;
126            return;
127        }
128        mValid = true;
129        mSessionComponentName = new ComponentName(service.getPackageName(),
130                mInfo.getSessionService());
131        mIWindowManager = IWindowManager.Stub.asInterface(
132                ServiceManager.getService(Context.WINDOW_SERVICE));
133        IntentFilter filter = new IntentFilter();
134        filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
135        mContext.registerReceiver(mBroadcastReceiver, filter, null, handler);
136    }
137
138    public boolean showSessionLocked(Bundle args, int flags,
139            IVoiceInteractionSessionShowCallback showCallback) {
140        if (mActiveSession == null) {
141            mActiveSession = new VoiceInteractionSessionConnection(mLock, mSessionComponentName,
142                    mUser, mContext, this, mInfo.getServiceInfo().applicationInfo.uid);
143        }
144        return mActiveSession.showLocked(args, flags, showCallback);
145    }
146
147    public boolean hideSessionLocked(int callingPid, int callingUid) {
148        if (mActiveSession != null) {
149            return mActiveSession.hideLocked();
150        }
151        return false;
152    }
153
154    public boolean deliverNewSessionLocked(int callingPid, int callingUid, IBinder token,
155            IVoiceInteractionSession session, IVoiceInteractor interactor) {
156        if (mActiveSession == null || token != mActiveSession.mToken) {
157            Slog.w(TAG, "deliverNewSession does not match active session");
158            return false;
159        }
160        mActiveSession.deliverNewSessionLocked(session, interactor);
161        return true;
162    }
163
164    public int startVoiceActivityLocked(int callingPid, int callingUid, IBinder token,
165            Intent intent, String resolvedType) {
166        try {
167            if (mActiveSession == null || token != mActiveSession.mToken) {
168                Slog.w(TAG, "startVoiceActivity does not match active session");
169                return ActivityManager.START_CANCELED;
170            }
171            if (!mActiveSession.mShown) {
172                Slog.w(TAG, "startVoiceActivity not allowed on hidden session");
173                return ActivityManager.START_CANCELED;
174            }
175            intent = new Intent(intent);
176            intent.addCategory(Intent.CATEGORY_VOICE);
177            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
178            return mAm.startVoiceActivity(mComponent.getPackageName(), callingPid, callingUid,
179                    intent, resolvedType, mActiveSession.mSession, mActiveSession.mInteractor,
180                    0, null, null, mUser);
181        } catch (RemoteException e) {
182            throw new IllegalStateException("Unexpected remote error", e);
183        }
184    }
185
186    public void setKeepAwakeLocked(int callingPid, int callingUid, IBinder token,
187            boolean keepAwake) {
188        try {
189            if (mActiveSession == null || token != mActiveSession.mToken) {
190                Slog.w(TAG, "setKeepAwake does not match active session");
191                return;
192            }
193            mAm.setVoiceKeepAwake(mActiveSession.mSession, keepAwake);
194        } catch (RemoteException e) {
195            throw new IllegalStateException("Unexpected remote error", e);
196        }
197    }
198
199    public void finishLocked(IBinder token) {
200        if (mActiveSession == null || token != mActiveSession.mToken) {
201            Slog.w(TAG, "finish does not match active session");
202            return;
203        }
204        mActiveSession.cancelLocked();
205        mActiveSession = null;
206    }
207
208    public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args) {
209        if (!mValid) {
210            pw.print("  NOT VALID: ");
211            if (mInfo == null) {
212                pw.println("no info");
213            } else {
214                pw.println(mInfo.getParseError());
215            }
216            return;
217        }
218        pw.print("  mComponent="); pw.println(mComponent.flattenToShortString());
219        pw.print("  Session service="); pw.println(mInfo.getSessionService());
220        pw.print("  Settings activity="); pw.println(mInfo.getSettingsActivity());
221        pw.print("  mBound="); pw.print(mBound);  pw.print(" mService="); pw.println(mService);
222        if (mActiveSession != null) {
223            pw.println("  Active session:");
224            mActiveSession.dump("    ", pw);
225        }
226    }
227
228    void startLocked() {
229        Intent intent = new Intent(VoiceInteractionService.SERVICE_INTERFACE);
230        intent.setComponent(mComponent);
231        mBound = mContext.bindServiceAsUser(intent, mConnection,
232                Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, new UserHandle(mUser));
233        if (!mBound) {
234            Slog.w(TAG, "Failed binding to voice interaction service " + mComponent);
235        }
236    }
237
238    public void launchVoiceAssistFromKeyguard() {
239        if (mService == null) {
240            Slog.w(TAG, "Not bound to voice interaction service " + mComponent);
241            return;
242        }
243        try {
244            mService.launchVoiceAssistFromKeyguard();
245        } catch (RemoteException e) {
246            Slog.w(TAG, "RemoteException while calling launchVoiceAssistFromKeyguard", e);
247        }
248    }
249
250    void shutdownLocked() {
251        // If there is an active session, cancel it to allow it to clean up its window and other
252        // state.
253        if (mActiveSession != null) {
254            mActiveSession.cancelLocked();
255            mActiveSession = null;
256        }
257        try {
258            if (mService != null) {
259                mService.shutdown();
260            }
261        } catch (RemoteException e) {
262            Slog.w(TAG, "RemoteException in shutdown", e);
263        }
264
265        if (mBound) {
266            mContext.unbindService(mConnection);
267            mBound = false;
268        }
269        if (mValid) {
270            mContext.unregisterReceiver(mBroadcastReceiver);
271        }
272    }
273
274    void notifySoundModelsChangedLocked() {
275        if (mService == null) {
276            Slog.w(TAG, "Not bound to voice interaction service " + mComponent);
277            return;
278        }
279        try {
280            mService.soundModelsChanged();
281        } catch (RemoteException e) {
282            Slog.w(TAG, "RemoteException while calling soundModelsChanged", e);
283        }
284    }
285
286    @Override
287    public void sessionConnectionGone(VoiceInteractionSessionConnection connection) {
288        synchronized (mLock) {
289            finishLocked(connection.mToken);
290        }
291    }
292}
293