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