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

Commit 4a792e33 authored by MingWei Liao's avatar MingWei Liao Committed by Android (Google) Code Review
Browse files

Merge "Restrict RecognitionService process capabilities" into main

parents 2ce9e8b0 449490c4
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;