VoiceInteractionManagerService.java revision c03c9167c2d9a1e22fb2b176b00a0524177fb037
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.Manifest;
20import android.app.ActivityManager;
21import android.content.ComponentName;
22import android.content.ContentResolver;
23import android.content.Context;
24import android.content.Intent;
25import android.content.pm.PackageManager;
26import android.database.ContentObserver;
27import android.os.Binder;
28import android.os.Bundle;
29import android.os.Handler;
30import android.os.IBinder;
31import android.os.Parcel;
32import android.os.RemoteException;
33import android.os.UserHandle;
34import android.provider.Settings;
35import android.service.voice.IVoiceInteractionService;
36import android.service.voice.IVoiceInteractionSession;
37import android.util.Slog;
38import com.android.internal.app.IVoiceInteractionManagerService;
39import com.android.internal.app.IVoiceInteractor;
40import com.android.internal.content.PackageMonitor;
41import com.android.internal.os.BackgroundThread;
42import com.android.server.SystemService;
43import com.android.server.UiThread;
44
45import java.io.FileDescriptor;
46import java.io.PrintWriter;
47
48
49/**
50 * SystemService that publishes an IVoiceInteractionManagerService.
51 */
52public class VoiceInteractionManagerService extends SystemService {
53
54    static final String TAG = "VoiceInteractionManagerService";
55
56    final Context mContext;
57    final ContentResolver mResolver;
58
59    public VoiceInteractionManagerService(Context context) {
60        super(context);
61        mContext = context;
62        mResolver = context.getContentResolver();
63    }
64
65    @Override
66    public void onStart() {
67        publishBinderService(Context.VOICE_INTERACTION_MANAGER_SERVICE, mServiceStub);
68    }
69
70    @Override
71    public void onBootPhase(int phase) {
72        if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
73            mServiceStub.systemRunning(isSafeMode());
74        }
75    }
76
77    @Override
78    public void onSwitchUser(int userHandle) {
79        mServiceStub.switchUser(userHandle);
80    }
81
82    // implementation entry point and binder service
83    private final VoiceInteractionManagerServiceStub mServiceStub
84            = new VoiceInteractionManagerServiceStub();
85
86    class VoiceInteractionManagerServiceStub extends IVoiceInteractionManagerService.Stub {
87
88        VoiceInteractionManagerServiceImpl mImpl;
89
90        private boolean mSafeMode;
91        private int mCurUser;
92
93        @Override
94        public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
95                throws RemoteException {
96            try {
97                return super.onTransact(code, data, reply, flags);
98            } catch (RuntimeException e) {
99                // The activity manager only throws security exceptions, so let's
100                // log all others.
101                if (!(e instanceof SecurityException)) {
102                    Slog.wtf(TAG, "VoiceInteractionManagerService Crash", e);
103                }
104                throw e;
105            }
106        }
107
108        public void systemRunning(boolean safeMode) {
109            mSafeMode = safeMode;
110
111            mPackageMonitor.register(mContext, BackgroundThread.getHandler().getLooper(),
112                    UserHandle.ALL, true);
113            new SettingsObserver(UiThread.getHandler());
114
115            synchronized (this) {
116                mCurUser = ActivityManager.getCurrentUser();
117                switchImplementationIfNeededLocked(false);
118            }
119        }
120
121        public void switchUser(int userHandle) {
122            synchronized (this) {
123                mCurUser = userHandle;
124                switchImplementationIfNeededLocked(false);
125            }
126        }
127
128        void switchImplementationIfNeededLocked(boolean force) {
129            if (!mSafeMode) {
130                String curService = Settings.Secure.getStringForUser(
131                        mResolver, Settings.Secure.VOICE_INTERACTION_SERVICE, mCurUser);
132                ComponentName serviceComponent = null;
133                if (curService != null && !curService.isEmpty()) {
134                    try {
135                        serviceComponent = ComponentName.unflattenFromString(curService);
136                    } catch (RuntimeException e) {
137                        Slog.wtf(TAG, "Bad voice interaction service name " + curService, e);
138                        serviceComponent = null;
139                    }
140                }
141                if (force || mImpl == null || mImpl.mUser != mCurUser
142                        || !mImpl.mComponent.equals(serviceComponent)) {
143                    if (mImpl != null) {
144                        mImpl.shutdownLocked();
145                    }
146                    if (serviceComponent != null) {
147                        mImpl = new VoiceInteractionManagerServiceImpl(mContext,
148                                UiThread.getHandler(), this, mCurUser, serviceComponent);
149                        mImpl.startLocked();
150                    } else {
151                        mImpl = null;
152                    }
153                }
154            }
155        }
156
157        @Override
158        public void startSession(IVoiceInteractionService service, Bundle args) {
159            synchronized (this) {
160                if (mImpl == null || mImpl.mService == null
161                        || service.asBinder() != mImpl.mService.asBinder()) {
162                    throw new SecurityException(
163                            "Caller is not the current voice interaction service");
164                }
165                final int callingPid = Binder.getCallingPid();
166                final int callingUid = Binder.getCallingUid();
167                final long caller = Binder.clearCallingIdentity();
168                try {
169                    mImpl.startSessionLocked(callingPid, callingUid, args);
170                } finally {
171                    Binder.restoreCallingIdentity(caller);
172                }
173            }
174        }
175
176        @Override
177        public boolean deliverNewSession(IBinder token, IVoiceInteractionSession session,
178                IVoiceInteractor interactor) {
179            synchronized (this) {
180                if (mImpl == null) {
181                    throw new SecurityException(
182                            "deliverNewSession without running voice interaction service");
183                }
184                final int callingPid = Binder.getCallingPid();
185                final int callingUid = Binder.getCallingUid();
186                final long caller = Binder.clearCallingIdentity();
187                try {
188                    return mImpl.deliverNewSessionLocked(callingPid, callingUid, token, session,
189                            interactor);
190                } finally {
191                    Binder.restoreCallingIdentity(caller);
192                }
193            }
194        }
195
196        @Override
197        public int startVoiceActivity(IBinder token, Intent intent, String resolvedType) {
198            synchronized (this) {
199                if (mImpl == null) {
200                    Slog.w(TAG, "startVoiceActivity without running voice interaction service");
201                    return ActivityManager.START_CANCELED;
202                }
203                final int callingPid = Binder.getCallingPid();
204                final int callingUid = Binder.getCallingUid();
205                final long caller = Binder.clearCallingIdentity();
206                try {
207                    return mImpl.startVoiceActivityLocked(callingPid, callingUid, token,
208                            intent, resolvedType);
209                } finally {
210                    Binder.restoreCallingIdentity(caller);
211                }
212            }
213        }
214
215        @Override
216        public void finish(IBinder token) {
217            synchronized (this) {
218                if (mImpl == null) {
219                    Slog.w(TAG, "finish without running voice interaction service");
220                    return;
221                }
222                final int callingPid = Binder.getCallingPid();
223                final int callingUid = Binder.getCallingUid();
224                final long caller = Binder.clearCallingIdentity();
225                try {
226                    mImpl.finishLocked(callingPid, callingUid, token);
227                } finally {
228                    Binder.restoreCallingIdentity(caller);
229                }
230            }
231        }
232
233        @Override
234        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
235            if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
236                    != PackageManager.PERMISSION_GRANTED) {
237                pw.println("Permission Denial: can't dump PowerManager from from pid="
238                        + Binder.getCallingPid()
239                        + ", uid=" + Binder.getCallingUid());
240                return;
241            }
242            synchronized (this) {
243                pw.println("VOICE INTERACTION MANAGER (dumpsys voiceinteraction)\n");
244                if (mImpl == null) {
245                    pw.println("  (No active implementation)");
246                    return;
247                }
248                mImpl.dumpLocked(fd, pw, args);
249            }
250        }
251
252        class SettingsObserver extends ContentObserver {
253            SettingsObserver(Handler handler) {
254                super(handler);
255                ContentResolver resolver = mContext.getContentResolver();
256                resolver.registerContentObserver(Settings.Secure.getUriFor(
257                        Settings.Secure.VOICE_INTERACTION_SERVICE), false, this);
258            }
259
260            @Override public void onChange(boolean selfChange) {
261                synchronized (VoiceInteractionManagerServiceStub.this) {
262                    switchImplementationIfNeededLocked(false);
263                }
264            }
265        }
266
267        PackageMonitor mPackageMonitor = new PackageMonitor() {
268            @Override
269            public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
270                return super.onHandleForceStop(intent, packages, uid, doit);
271            }
272
273            @Override
274            public void onHandleUserStop(Intent intent, int userHandle) {
275            }
276
277            @Override
278            public void onPackageDisappeared(String packageName, int reason) {
279            }
280
281            @Override
282            public void onPackageAppeared(String packageName, int reason) {
283                if (mImpl != null && packageName.equals(mImpl.mComponent.getPackageName())) {
284                    switchImplementationIfNeededLocked(true);
285                }
286            }
287
288            @Override
289            public void onPackageModified(String packageName) {
290            }
291
292            @Override
293            public void onSomePackagesChanged() {
294            }
295        };
296    }
297}
298