Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a351ab96 authored by Dianne Hackborn's avatar Dianne Hackborn
Browse files

Fix issues 16739817 and 16709247 in voice interaction service.

Issue #16739817 VIS doesn't start for non-primary user(s)
Issue #16709247 GSA is not the default voice interaction agent

These are both fixed by getting rid of the existing code for applying
the default voice recognizer, moving it in to the voice interaction
manager service, and extending it to also set up the default voice
interaction service.

Change-Id: If8d5936c28aebfa7eff77c8d99241c3a2ffdb0a4
parent d78e8114
Loading
Loading
Loading
Loading
+8 −1
Original line number Diff line number Diff line
@@ -17,13 +17,14 @@
package android.service.voice;

import android.Manifest;
import android.app.AppGlobals;
import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.speech.RecognitionService;
import android.os.RemoteException;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Xml;
@@ -48,6 +49,12 @@ public class VoiceInteractionServiceInfo {
        this(pm, pm.getServiceInfo(comp, PackageManager.GET_META_DATA));
    }

    public VoiceInteractionServiceInfo(PackageManager pm, ComponentName comp, int userHandle)
            throws PackageManager.NameNotFoundException, RemoteException {
        this(pm, AppGlobals.getPackageManager().getServiceInfo(comp,
                PackageManager.GET_META_DATA, userHandle));
    }

    public VoiceInteractionServiceInfo(PackageManager pm, ServiceInfo si) {
        if (!Manifest.permission.BIND_VOICE_INTERACTION.equals(si.permission)) {
            mParseError = "Service does not require permission "
+0 −169
Original line number Diff line number Diff line
/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server;

import com.android.internal.content.PackageMonitor;

import android.app.AppGlobals;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.IPackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.os.Binder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
import android.speech.RecognitionService;
import android.text.TextUtils;
import android.util.Slog;

import java.util.List;

public class RecognitionManagerService extends Binder {
    final static String TAG = "RecognitionManagerService";

    private final Context mContext;
    private final MyPackageMonitor mMonitor;
    private final IPackageManager mIPm;

    private static final boolean DEBUG = false;

    class MyPackageMonitor extends PackageMonitor {
        public void onSomePackagesChanged() {
            int userHandle = getChangingUserId();
            if (DEBUG) Slog.i(TAG, "onSomePackagesChanged user=" + userHandle);
            ComponentName comp = getCurRecognizer(userHandle);
            if (comp == null) {
                if (anyPackagesAppearing()) {
                    comp = findAvailRecognizer(null, userHandle);
                    if (comp != null) {
                        setCurRecognizer(comp, userHandle);
                    }
                }
                return;
            }

            int change = isPackageDisappearing(comp.getPackageName()); 
            if (change == PACKAGE_PERMANENT_CHANGE
                    || change == PACKAGE_TEMPORARY_CHANGE) {
                setCurRecognizer(findAvailRecognizer(null, userHandle), userHandle);
                
            } else if (isPackageModified(comp.getPackageName())) {
                setCurRecognizer(findAvailRecognizer(comp.getPackageName(), userHandle),
                        userHandle);
            }
        }
    }

    RecognitionManagerService(Context context) {
        mContext = context;
        mMonitor = new MyPackageMonitor();
        mMonitor.register(context, null, UserHandle.ALL, true);
        mIPm = AppGlobals.getPackageManager();
        IntentFilter filter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
        filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
        mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
                filter, null, null);
    }

    public void systemReady() {
        initForUser(UserHandle.USER_OWNER);
    }

    private void initForUser(int userHandle) {
        if (DEBUG) Slog.i(TAG, "initForUser user=" + userHandle);
        ComponentName comp = getCurRecognizer(userHandle);
        ServiceInfo info = null;
        if (comp != null) {
            // See if the current recognizer is still available.
            try {
                info = mIPm.getServiceInfo(comp, 0, userHandle);
            } catch (RemoteException e) {
            }
        }
        if (info == null) {
            comp = findAvailRecognizer(null, userHandle);
            if (comp != null) {
                setCurRecognizer(comp, userHandle);
            }
        }
    }

    ComponentName findAvailRecognizer(String prefPackage, int userHandle) {
        List<ResolveInfo> available =
                mContext.getPackageManager().queryIntentServicesAsUser(
                        new Intent(RecognitionService.SERVICE_INTERFACE), 0, userHandle);
        int numAvailable = available.size();

        if (numAvailable == 0) {
            Slog.w(TAG, "no available voice recognition services found for user " + userHandle);
            return null;
        } else {
            if (prefPackage != null) {
                for (int i=0; i<numAvailable; i++) {
                    ServiceInfo serviceInfo = available.get(i).serviceInfo;
                    if (prefPackage.equals(serviceInfo.packageName)) {
                        return new ComponentName(serviceInfo.packageName, serviceInfo.name);
                    }
                }
            }
            if (numAvailable > 1) {
                Slog.w(TAG, "more than one voice recognition service found, picking first");
            }

            ServiceInfo serviceInfo = available.get(0).serviceInfo;
            return new ComponentName(serviceInfo.packageName, serviceInfo.name);
        }
    }

    ComponentName getCurRecognizer(int userHandle) {
        String curRecognizer = Settings.Secure.getStringForUser(
                mContext.getContentResolver(),
                Settings.Secure.VOICE_RECOGNITION_SERVICE, userHandle);
        if (TextUtils.isEmpty(curRecognizer)) {
            return null;
        }
        if (DEBUG) Slog.i(TAG, "getCurRecognizer curRecognizer=" + curRecognizer
                + " user=" + userHandle);
        return ComponentName.unflattenFromString(curRecognizer);
    }

    void setCurRecognizer(ComponentName comp, int userHandle) {
        Settings.Secure.putStringForUser(mContext.getContentResolver(),
                Settings.Secure.VOICE_RECOGNITION_SERVICE,
                comp != null ? comp.flattenToShortString() : "", userHandle);
        if (DEBUG) Slog.i(TAG, "setCurRecognizer comp=" + comp
                + " user=" + userHandle);
    }

    BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (DEBUG) Slog.i(TAG, "received " + action);
            if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
                int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
                if (userHandle > 0) {
                    initForUser(userHandle);
                }
            }
        }
    };
}
+1 −1
Original line number Diff line number Diff line
@@ -17327,7 +17327,7 @@ public final class ActivityManagerService extends ActivityManagerNative
                    // Booting up a new user, need to tell system services about it.
                    // Note that this is on the same handler as scheduling of broadcasts,
                    // which is important because it needs to go first.
                    mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_START_MSG, userId));
                    mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_START_MSG, userId, 0));
                }
                if (foreground) {
+0 −14
Original line number Diff line number Diff line
@@ -402,7 +402,6 @@ public final class SystemServer {
        BluetoothManagerService bluetooth = null;
        UsbService usb = null;
        SerialService serial = null;
        RecognitionManagerService recognition = null;
        NetworkTimeUpdateService networkTimeUpdater = null;
        CommonTimeManagementService commonTimeMgmtService = null;
        InputManagerService inputManager = null;
@@ -843,13 +842,6 @@ public final class SystemServer {
                    mSystemServiceManager.startService(APPWIDGET_SERVICE_CLASS);
                }

                try {
                    Slog.i(TAG, "Recognition Service");
                    recognition = new RecognitionManagerService(context);
                } catch (Throwable e) {
                    reportWtf("starting Recognition Service", e);
                }

                if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_VOICE_RECOGNIZERS)) {
                    mSystemServiceManager.startService(VOICE_RECOGNITION_MANAGER_SERVICE_CLASS);
                }
@@ -1046,7 +1038,6 @@ public final class SystemServer {
        final NetworkScoreService networkScoreF = networkScore;
        final WallpaperManagerService wallpaperF = wallpaper;
        final InputMethodManagerService immF = imm;
        final RecognitionManagerService recognitionF = recognition;
        final LocationManagerService locationF = location;
        final CountryDetectorService countryDetectorF = countryDetector;
        final NetworkTimeUpdateService networkTimeUpdaterF = networkTimeUpdater;
@@ -1116,11 +1107,6 @@ public final class SystemServer {
                } catch (Throwable e) {
                    reportWtf("making Connectivity Service ready", e);
                }
                try {
                    if (recognitionF != null) recognitionF.systemReady();
                } catch (Throwable e) {
                    reportWtf("making Recognition Service ready", e);
                }
                try {
                    if (audioServiceF != null) audioServiceF.systemReady();
                } catch (Throwable e) {
+215 −13
Original line number Diff line number Diff line
@@ -18,11 +18,16 @@ package com.android.server.voiceinteraction;

import android.Manifest;
import android.app.ActivityManager;
import android.app.AppGlobals;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.database.ContentObserver;
import android.hardware.soundtrigger.IRecognitionStatusCallback;
import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
@@ -38,7 +43,11 @@ import android.os.UserHandle;
import android.provider.Settings;
import android.service.voice.IVoiceInteractionService;
import android.service.voice.IVoiceInteractionSession;
import android.service.voice.VoiceInteractionService;
import android.service.voice.VoiceInteractionServiceInfo;
import android.speech.RecognitionService;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Slog;

import com.android.internal.app.IVoiceInteractionManagerService;
@@ -50,13 +59,14 @@ import com.android.server.UiThread;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.List;

/**
 * SystemService that publishes an IVoiceInteractionManagerService.
 */
public class VoiceInteractionManagerService extends SystemService {

    static final String TAG = "VoiceInteractionManagerService";
    static final boolean DEBUG = false;

    final Context mContext;
    final ContentResolver mResolver;
@@ -84,6 +94,11 @@ public class VoiceInteractionManagerService extends SystemService {
        }
    }

    @Override
    public void onStartUser(int userHandle) {
        mServiceStub.initForUser(userHandle);
    }

    @Override
    public void onSwitchUser(int userHandle) {
        mServiceStub.switchUser(userHandle);
@@ -115,6 +130,60 @@ public class VoiceInteractionManagerService extends SystemService {
            }
        }

        public void initForUser(int userHandle) {
            if (DEBUG) Slog.i(TAG, "initForUser user=" + userHandle);
            ComponentName curInteractor = getCurInteractor(userHandle);
            ComponentName curRecognizer = getCurRecognizer(userHandle);
            if (curRecognizer != null) {
                // If we already have at least a recognizer, then we probably want to
                // leave things as they are...  unless something has disappeared.
                IPackageManager pm = AppGlobals.getPackageManager();
                ServiceInfo interactorInfo = null;
                ServiceInfo recognizerInfo = null;
                try {
                    recognizerInfo = pm.getServiceInfo(curRecognizer, 0, userHandle);
                    if (curInteractor != null) {
                        interactorInfo = pm.getServiceInfo(curInteractor, 0, userHandle);
                    }
                } catch (RemoteException e) {
                }
                // If the apps for the currently set components still exist, then all is okay.
                if (recognizerInfo != null && (curInteractor == null || interactorInfo != null)) {
                    return;
                }
            }

            // Initializing settings, look for an interactor first.
            curInteractor = findAvailInteractor(userHandle);
            if (curInteractor != null) {
                try {
                    VoiceInteractionServiceInfo info = new VoiceInteractionServiceInfo(
                            mContext.getPackageManager(), curInteractor, userHandle);
                    if (info.getParseError() == null) {
                        setCurInteractor(curInteractor, userHandle);
                        if (info.getRecognitionService() != null) {
                            // Eventually it will be an error to not specify this.
                            curRecognizer = new ComponentName(info.getServiceInfo().packageName,
                                    info.getRecognitionService());
                            setCurRecognizer(curRecognizer, userHandle);
                            return;
                        }
                    } else {
                        Slog.w(TAG, "Bad interaction service " + curInteractor + ": "
                                + info.getParseError());
                    }
                } catch (PackageManager.NameNotFoundException e) {
                } catch (RemoteException e) {
                }
            }

            // No voice interactor, we'll just set up a simple recognizer.
            curRecognizer = findAvailRecognizer(null, userHandle);
            if (curRecognizer != null) {
                setCurRecognizer(curRecognizer, userHandle);
            }
        }

        public void systemRunning(boolean safeMode) {
            mSafeMode = safeMode;

@@ -165,6 +234,105 @@ public class VoiceInteractionManagerService extends SystemService {
            }
        }

        ComponentName findAvailInteractor(int userHandle) {
            List<ResolveInfo> available =
                    mContext.getPackageManager().queryIntentServicesAsUser(
                            new Intent(VoiceInteractionService.SERVICE_INTERFACE), 0, userHandle);
            int numAvailable = available.size();

            if (numAvailable == 0) {
                Slog.w(TAG, "no available voice interaction services found for user " + userHandle);
                return null;
            } else {
                // Find first system package.  We never want to allow third party services to
                // be automatically selected, because those require approval of the user.
                ServiceInfo serviceInfo = null;
                for (int i=0; i<numAvailable; i++) {
                    ServiceInfo cur = available.get(i).serviceInfo;
                    if ((cur.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
                        if (serviceInfo == null) {
                            serviceInfo = cur;
                        } else {
                            Slog.w(TAG, "more than one voice interaction service, picking first "
                                    + new ComponentName(serviceInfo.packageName, serviceInfo.name)
                                    + " over "
                                    + new ComponentName(cur.packageName, cur.name));
                        }
                    }
                }

                return serviceInfo != null ?
                        new ComponentName(serviceInfo.packageName, serviceInfo.name) : null;
            }
        }

        ComponentName getCurInteractor(int userHandle) {
            String curInteractor = Settings.Secure.getStringForUser(
                    mContext.getContentResolver(),
                    Settings.Secure.VOICE_INTERACTION_SERVICE, userHandle);
            if (TextUtils.isEmpty(curInteractor)) {
                return null;
            }
            if (DEBUG) Slog.i(TAG, "getCurInteractor curInteractor=" + curInteractor
                    + " user=" + userHandle);
            return ComponentName.unflattenFromString(curInteractor);
        }

        void setCurInteractor(ComponentName comp, int userHandle) {
            Settings.Secure.putStringForUser(mContext.getContentResolver(),
                    Settings.Secure.VOICE_INTERACTION_SERVICE,
                    comp != null ? comp.flattenToShortString() : "", userHandle);
            if (DEBUG) Slog.i(TAG, "setCurInteractor comp=" + comp
                    + " user=" + userHandle);
        }

        ComponentName findAvailRecognizer(String prefPackage, int userHandle) {
            List<ResolveInfo> available =
                    mContext.getPackageManager().queryIntentServicesAsUser(
                            new Intent(RecognitionService.SERVICE_INTERFACE), 0, userHandle);
            int numAvailable = available.size();

            if (numAvailable == 0) {
                Slog.w(TAG, "no available voice recognition services found for user " + userHandle);
                return null;
            } else {
                if (prefPackage != null) {
                    for (int i=0; i<numAvailable; i++) {
                        ServiceInfo serviceInfo = available.get(i).serviceInfo;
                        if (prefPackage.equals(serviceInfo.packageName)) {
                            return new ComponentName(serviceInfo.packageName, serviceInfo.name);
                        }
                    }
                }
                if (numAvailable > 1) {
                    Slog.w(TAG, "more than one voice recognition service found, picking first");
                }

                ServiceInfo serviceInfo = available.get(0).serviceInfo;
                return new ComponentName(serviceInfo.packageName, serviceInfo.name);
            }
        }

        ComponentName getCurRecognizer(int userHandle) {
            String curRecognizer = Settings.Secure.getStringForUser(
                    mContext.getContentResolver(),
                    Settings.Secure.VOICE_RECOGNITION_SERVICE, userHandle);
            if (TextUtils.isEmpty(curRecognizer)) {
                return null;
            }
            if (DEBUG) Slog.i(TAG, "getCurRecognizer curRecognizer=" + curRecognizer
                    + " user=" + userHandle);
            return ComponentName.unflattenFromString(curRecognizer);
        }

        void setCurRecognizer(ComponentName comp, int userHandle) {
            Settings.Secure.putStringForUser(mContext.getContentResolver(),
                    Settings.Secure.VOICE_RECOGNITION_SERVICE,
                    comp != null ? comp.flattenToShortString() : "", userHandle);
            if (DEBUG) Slog.i(TAG, "setCurRecognizer comp=" + comp
                    + " user=" + userHandle);
        }

        @Override
        public void startSession(IVoiceInteractionService service, Bundle args) {
            synchronized (this) {
@@ -461,22 +629,56 @@ public class VoiceInteractionManagerService extends SystemService {
            }

            @Override
            public void onPackageDisappeared(String packageName, int reason) {
            public void onSomePackagesChanged() {
                int userHandle = getChangingUserId();
                if (DEBUG) Slog.i(TAG, "onSomePackagesChanged user=" + userHandle);

                ComponentName curInteractor = getCurInteractor(userHandle);
                ComponentName curRecognizer = getCurRecognizer(userHandle);
                if (curRecognizer == null) {
                    // Could a new recognizer appear when we don't have one pre-installed?
                    if (anyPackagesAppearing()) {
                        curRecognizer = findAvailRecognizer(null, userHandle);
                        if (curRecognizer != null) {
                            setCurRecognizer(curRecognizer, userHandle);
                        }
                    }
                    return;
                }

            @Override
            public void onPackageAppeared(String packageName, int reason) {
                if (mImpl != null && packageName.equals(mImpl.mComponent.getPackageName())) {
                if (curInteractor != null) {
                    int change = isPackageDisappearing(curInteractor.getPackageName());
                    if (change == PACKAGE_PERMANENT_CHANGE) {
                        // The currently set interactor is permanently gone; fall back to
                        // the default config.
                        setCurInteractor(null, userHandle);
                        setCurRecognizer(null, userHandle);
                        initForUser(userHandle);
                        return;
                    }

                    change = isPackageAppearing(curInteractor.getPackageName());
                    if (change != PACKAGE_UNCHANGED) {
                        // If current interactor is now appearing, for any reason, then
                        // restart our connection with it.
                        if (mImpl != null && curInteractor.getPackageName().equals(
                                mImpl.mComponent.getPackageName())) {
                            switchImplementationIfNeededLocked(true);
                        }
                    }

            @Override
            public void onPackageModified(String packageName) {
                    return;
                }

            @Override
            public void onSomePackagesChanged() {
                // There is no interactor, so just deal with a simple recognizer.
                int change = isPackageDisappearing(curRecognizer.getPackageName());
                if (change == PACKAGE_PERMANENT_CHANGE
                        || change == PACKAGE_TEMPORARY_CHANGE) {
                    setCurRecognizer(findAvailRecognizer(null, userHandle), userHandle);

                } else if (isPackageModified(curRecognizer.getPackageName())) {
                    setCurRecognizer(findAvailRecognizer(curRecognizer.getPackageName(),
                            userHandle), userHandle);
                }
            }
        };
    }