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