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

Commit 4b3f4355 authored by Ahaan Ugale's avatar Ahaan Ugale Committed by Automerger Merge Worker
Browse files

Merge "Unset default reco if selectableAsDefault=false." into sc-dev am: 5395b1a4

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/13963741

Change-Id: I5299a3b2a59c12f041d4bfd99610a712ab943713
parents 590a6b89 5395b1a4
Loading
Loading
Loading
Loading
+148 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.voiceinteraction;

import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
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.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Xml;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

// TODO: Move this class somewhere else, along with the default recognizer logic in
//  VoiceInteractionManagerService.
// TODO: Use this class in com.android.settings.applications.assist.VoiceInputHelper.

/**
 * {@link ServiceInfo} and parsed metadata for a {@link RecognitionService}.
 */
class RecognitionServiceInfo {
    private static final String TAG = "RecognitionServiceInfo";

    private final String mParseError;
    private final ServiceInfo mServiceInfo;
    private final boolean mSelectableAsDefault;

    /**
     * Queries the valid recognition services available for the user.
     */
    static List<RecognitionServiceInfo> getAvailableServices(
            @NonNull Context context, @UserIdInt int user) {
        List<RecognitionServiceInfo> services = new ArrayList<>();

        List<ResolveInfo> resolveInfos =
                context.getPackageManager().queryIntentServicesAsUser(
                        new Intent(RecognitionService.SERVICE_INTERFACE),
                        PackageManager.MATCH_DIRECT_BOOT_AWARE
                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
                        user);
        for (ResolveInfo resolveInfo : resolveInfos) {
            RecognitionServiceInfo service =
                    parseInfo(context.getPackageManager(), resolveInfo.serviceInfo);
            if (!TextUtils.isEmpty(service.mParseError)) {
                Log.w(TAG, "Parse error in getAvailableServices: " + service.mParseError);
                // We still use the recognizer to preserve pre-existing behavior.
            }
            services.add(service);
        }
        return services;
    }

    /**
     * Loads the service metadata published by the component. Success is indicated by {@link
     * #getParseError()}.
     *
     * @param pm A PackageManager from which the XML can be loaded; usually the
     *         PackageManager from which {@code si} was originally retrieved.
     * @param si The {@link android.speech.RecognitionService} info.
     */
    static RecognitionServiceInfo parseInfo(@NonNull PackageManager pm, @NonNull ServiceInfo si) {
        String parseError = "";
        boolean selectableAsDefault = true; // default
        try (XmlResourceParser parser = si.loadXmlMetaData(
                pm,
                RecognitionService.SERVICE_META_DATA)) {
            if (parser == null) {
                parseError = "No " + RecognitionService.SERVICE_META_DATA
                        + " meta-data for " + si.packageName;
                return new RecognitionServiceInfo(si, selectableAsDefault, parseError);
            }
            Resources res = pm.getResourcesForApplication(si.applicationInfo);
            AttributeSet attrs = Xml.asAttributeSet(parser);

            int type = 0;
            while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) {
                type = parser.next();
            }

            String nodeName = parser.getName();
            if (!"recognition-service".equals(nodeName)) {
                throw new XmlPullParserException(
                        "Meta-data does not start with recognition-service tag");
            }

            TypedArray values =
                    res.obtainAttributes(
                            attrs, com.android.internal.R.styleable.RecognitionService);
            selectableAsDefault =
                    values.getBoolean(
                            com.android.internal.R.styleable.RecognitionService_selectableAsDefault,
                            selectableAsDefault);
            values.recycle();
        } catch (XmlPullParserException | IOException | PackageManager.NameNotFoundException e) {
            parseError = "Error parsing recognition service meta-data: " + e;
        }
        return new RecognitionServiceInfo(si, selectableAsDefault, parseError);
    }

    private RecognitionServiceInfo(
            @NonNull ServiceInfo si, boolean selectableAsDefault, @NonNull String parseError) {
        mServiceInfo = si;
        mSelectableAsDefault = selectableAsDefault;
        mParseError = parseError;
    }

    @NonNull
    public String getParseError() {
        return mParseError;
    }

    @NonNull
    public ServiceInfo getServiceInfo() {
        return mServiceInfo;
    }

    public boolean isSelectableAsDefault() {
        return mSelectableAsDefault;
    }
}
+49 −13
Original line number Diff line number Diff line
@@ -74,7 +74,6 @@ import android.service.voice.VoiceInteractionManagerInternal;
import android.service.voice.VoiceInteractionService;
import android.service.voice.VoiceInteractionServiceInfo;
import android.service.voice.VoiceInteractionSession;
import android.speech.RecognitionService;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -106,6 +105,7 @@ import com.android.server.wm.ActivityTaskManagerInternal;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
@@ -407,9 +407,30 @@ public class VoiceInteractionManagerService extends SystemService {
                ComponentName curInteractor = !TextUtils.isEmpty(curInteractorStr)
                        ? ComponentName.unflattenFromString(curInteractorStr) : null;
                try {
                    recognizerInfo = pm.getServiceInfo(curRecognizer,
                    recognizerInfo = pm.getServiceInfo(
                            curRecognizer,
                            PackageManager.MATCH_DIRECT_BOOT_AWARE
                                    | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userHandle);
                                    | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
                                    | PackageManager.GET_META_DATA,
                            userHandle);
                    if (recognizerInfo != null) {
                        RecognitionServiceInfo rsi =
                                RecognitionServiceInfo.parseInfo(
                                        mContext.getPackageManager(), recognizerInfo);
                        if (!TextUtils.isEmpty(rsi.getParseError())) {
                            Log.w(TAG, "Parse error in getAvailableServices: "
                                    + rsi.getParseError());
                            // We still use the recognizer to preserve pre-existing behavior.
                        }
                        if (!rsi.isSelectableAsDefault()) {
                            if (DEBUG) {
                                Slog.d(TAG, "Found non selectableAsDefault recognizer as"
                                        + " default. Unsetting the default and looking for another"
                                        + " one.");
                            }
                            recognizerInfo = null;
                        }
                    }
                    if (curInteractor != null) {
                        interactorInfo = pm.getServiceInfo(curInteractor,
                                PackageManager.MATCH_DIRECT_BOOT_AWARE
@@ -653,19 +674,23 @@ public class VoiceInteractionManagerService extends SystemService {
                prefPackage = getDefaultRecognizer();
            }

            List<ResolveInfo> available =
                    mContext.getPackageManager().queryIntentServicesAsUser(
                            new Intent(RecognitionService.SERVICE_INTERFACE),
                            PackageManager.MATCH_DIRECT_BOOT_AWARE
                                    | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userHandle);
            int numAvailable = available.size();
            if (numAvailable == 0) {
            List<RecognitionServiceInfo> available =
                    RecognitionServiceInfo.getAvailableServices(mContext, userHandle);
            if (available.size() == 0) {
                Slog.w(TAG, "no available voice recognition services found for user " + userHandle);
                return null;
            } else {
                List<RecognitionServiceInfo> nonSelectableAsDefault =
                        removeNonSelectableAsDefault(available);
                if (available.size() == 0) {
                    Slog.w(TAG, "No selectableAsDefault recognition services found for user "
                            + userHandle + ". Falling back to non selectableAsDefault ones.");
                    available = nonSelectableAsDefault;
                }
                int numAvailable = available.size();
                if (prefPackage != null) {
                    for (int i = 0; i < numAvailable; i++) {
                        ServiceInfo serviceInfo = available.get(i).serviceInfo;
                        ServiceInfo serviceInfo = available.get(i).getServiceInfo();
                        if (prefPackage.equals(serviceInfo.packageName)) {
                            return new ComponentName(serviceInfo.packageName, serviceInfo.name);
                        }
@@ -675,11 +700,22 @@ public class VoiceInteractionManagerService extends SystemService {
                    Slog.w(TAG, "more than one voice recognition service found, picking first");
                }

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

        private List<RecognitionServiceInfo> removeNonSelectableAsDefault(
                List<RecognitionServiceInfo> services) {
            List<RecognitionServiceInfo> nonSelectableAsDefault = new ArrayList<>();
            for (int i = services.size() - 1; i >= 0; i--) {
                if (!services.get(i).isSelectableAsDefault()) {
                    nonSelectableAsDefault.add(services.remove(i));
                }
            }
            return nonSelectableAsDefault;
        }

        @Nullable
        public String getDefaultRecognizer() {
            String recognizer = mContext.getString(R.string.config_systemSpeechRecognizer);