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

Commit 449490c4 authored by MingWei's avatar MingWei Committed by MingWei Liao
Browse files

Restrict RecognitionService process capabilities

Only provides certain privileged recognition service the privilege
process capabilities from system server. For recognition service
provided by client apps, it needs to stay in foreground in order to keep
the while-in-use permission.

Test: Cts
Bug: 307947153
Change-Id: Idd702017c4a4f7cc63b591660b599eccfa61b956
parent 98a3f121
Loading
Loading
Loading
Loading
+14 −4
Original line number Diff line number Diff line
@@ -68,12 +68,14 @@ final class RemoteSpeechRecognitionService extends ServiceConnector.Impl<IRecogn
    private final ComponentName mComponentName;

    RemoteSpeechRecognitionService(
            Context context, ComponentName serviceName, int userId, int callingUid) {
            Context context,
            ComponentName serviceName,
            int userId,
            int callingUid,
            boolean isPrivileged) {
        super(context,
                new Intent(RecognitionService.SERVICE_INTERFACE).setComponent(serviceName),
                Context.BIND_AUTO_CREATE
                        | Context.BIND_FOREGROUND_SERVICE
                        | Context.BIND_INCLUDE_CAPABILITIES,
                getBindingFlags(isPrivileged),
                userId,
                IRecognitionService.Stub::asInterface);

@@ -85,6 +87,14 @@ final class RemoteSpeechRecognitionService extends ServiceConnector.Impl<IRecogn
        }
    }

    private static int getBindingFlags(boolean isPrivileged) {
        int bindingFlags = Context.BIND_AUTO_CREATE;
        if (isPrivileged) {
            bindingFlags |= Context.BIND_INCLUDE_CAPABILITIES | Context.BIND_FOREGROUND_SERVICE;
        }
        return bindingFlags;
    }

    ComponentName getServiceComponentName() {
        return mComponentName;
    }
+63 −1
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.app.AppGlobals;
import android.content.AttributionSource;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
@@ -31,6 +32,7 @@ import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
import android.permission.PermissionManager;
import android.provider.Settings;
import android.speech.IModelDownloadListener;
import android.speech.IRecognitionListener;
import android.speech.IRecognitionService;
@@ -311,9 +313,22 @@ final class SpeechRecognitionManagerServiceImpl extends
                return null;
            }

            final boolean isPrivileged;
            if (serviceComponent == null) {
                isPrivileged = false;
            } else {
                // Only certain privileged recognition service can obtain process capabilities
                // from persistent process to hold while-in-use permission in the background.
                isPrivileged = checkPrivilege(serviceComponent);
            }

            RemoteSpeechRecognitionService service =
                    new RemoteSpeechRecognitionService(
                            getContext(), serviceComponent, getUserId(), callingUid);
                            getContext(),
                            serviceComponent,
                            getUserId(),
                            callingUid,
                            isPrivileged);

            Set<RemoteSpeechRecognitionService> valuesByCaller =
                    mRemoteServicesByUid.computeIfAbsent(callingUid, key -> new HashSet<>());
@@ -328,6 +343,53 @@ final class SpeechRecognitionManagerServiceImpl extends
        }
    }

    /**
     * Checks if the given service component should have privileged binding flags when created. Only
     * a service component that matches with any of the following condition would be granted:
     *
     * <ul>
     *     <li>A default recognition service component.</li>
     *     <li>An on-device recognition service component.</li>
     *     <li>A pre-installed recognition service component.</li>
     * </ul>
     */
    @GuardedBy("mLock")
    private boolean checkPrivilege(@NonNull ComponentName serviceComponent) {
        final ComponentName defaultComponent = getDefaultRecognitionServiceComponent();
        final ComponentName onDeviceComponent = getOnDeviceComponentNameLocked();
        final boolean preinstalled = isPreinstalledApp(serviceComponent);
        return serviceComponent.equals(defaultComponent)
                || serviceComponent.equals(onDeviceComponent)
                || preinstalled;
    }

    private boolean isPreinstalledApp(@NonNull ComponentName serviceComponent) {
        PackageManager pm = getContext().getPackageManager();
        if (pm == null) {
            return false;
        }

        try {
            ApplicationInfo info = pm.getApplicationInfoAsUser(serviceComponent.getPackageName(),
                    PackageManager.MATCH_SYSTEM_ONLY, getUserId());
            return (info.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
        } catch (PackageManager.NameNotFoundException e) {
            return false;
        }
    }

    @Nullable
    private ComponentName getDefaultRecognitionServiceComponent() {
        String componentName = Settings.Secure.getStringForUser(
                getContext().getContentResolver(),
                Settings.Secure.VOICE_RECOGNITION_SERVICE,
                getUserId());
        if (componentName == null) {
            return null;
        }
        return ComponentName.unflattenFromString(componentName);
    }

    private boolean componentMapsToRecognitionService(@NonNull ComponentName serviceComponent) {
        List<ResolveInfo> resolveInfos;