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

Commit 5b7c195b authored by Nick Moukhine's avatar Nick Moukhine
Browse files

Follow-up fixes to MusicRecognitionManagerService

Added a check to only allow the default MusicRecognitionService to be called, unless the caller is the same app that hosts the MusicRecognitionService, as is the case in tests.

Refactored MusicRecognitionManagerPerUserService to not hold the client's callback as a field to prevent leaks and needing to null it out (then requiring null checks).  Instead, it's always a local variable or a final field in ephemeral objects.

Bug: 174744852
Bug: 168696277
Test: atest CtsMusicRecognitionTestCases
Change-Id: I70ff3c38da7096c60f82fe75b47674b4b233e672
parent a0334be8
Loading
Loading
Loading
Loading
+28 −16
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.server.musicrecognition;

import static android.media.musicrecognition.MusicRecognitionManager.RECOGNITION_FAILED_AUDIO_UNAVAILABLE;
import static android.media.musicrecognition.MusicRecognitionManager.RECOGNITION_FAILED_SERVICE_KILLED;
import static android.media.musicrecognition.MusicRecognitionManager.RECOGNITION_FAILED_SERVICE_UNAVAILABLE;
import static android.media.musicrecognition.MusicRecognitionManager.RecognitionFailureCode;
@@ -64,10 +65,6 @@ public final class MusicRecognitionManagerPerUserService extends
    @GuardedBy("mLock")
    private RemoteMusicRecognitionService mRemoteService;

    private MusicRecognitionServiceCallback mRemoteServiceCallback =
            new MusicRecognitionServiceCallback();
    private IMusicRecognitionManagerCallback mCallback;

    MusicRecognitionManagerPerUserService(
            @NonNull MusicRecognitionManagerService primary,
            @NonNull Object lock, int userId) {
@@ -100,7 +97,8 @@ public final class MusicRecognitionManagerPerUserService extends

    @GuardedBy("mLock")
    @Nullable
    private RemoteMusicRecognitionService ensureRemoteServiceLocked() {
    private RemoteMusicRecognitionService ensureRemoteServiceLocked(
            IMusicRecognitionManagerCallback clientCallback) {
        if (mRemoteService == null) {
            final String serviceName = getComponentNameLocked();
            if (serviceName == null) {
@@ -113,7 +111,8 @@ public final class MusicRecognitionManagerPerUserService extends

            mRemoteService = new RemoteMusicRecognitionService(getContext(),
                    serviceComponent, mUserId, this,
                    mRemoteServiceCallback, mMaster.isBindInstantServiceAllowed(),
                    new MusicRecognitionServiceCallback(clientCallback),
                    mMaster.isBindInstantServiceAllowed(),
                    mMaster.verbose);
        }

@@ -130,13 +129,14 @@ public final class MusicRecognitionManagerPerUserService extends
            @NonNull IBinder callback) {
        int maxAudioLengthSeconds = Math.min(recognitionRequest.getMaxAudioLengthSeconds(),
                MAX_STREAMING_SECONDS);
        mCallback = IMusicRecognitionManagerCallback.Stub.asInterface(callback);
        IMusicRecognitionManagerCallback clientCallback =
                IMusicRecognitionManagerCallback.Stub.asInterface(callback);
        AudioRecord audioRecord = createAudioRecord(recognitionRequest, maxAudioLengthSeconds);

        mRemoteService = ensureRemoteServiceLocked();
        mRemoteService = ensureRemoteServiceLocked(clientCallback);
        if (mRemoteService == null) {
            try {
                mCallback.onRecognitionFailed(
                clientCallback.onRecognitionFailed(
                        RECOGNITION_FAILED_SERVICE_UNAVAILABLE);
            } catch (RemoteException e) {
                // Ignored.
@@ -147,7 +147,8 @@ public final class MusicRecognitionManagerPerUserService extends
        Pair<ParcelFileDescriptor, ParcelFileDescriptor> clientPipe = createPipe();
        if (clientPipe == null) {
            try {
                mCallback.onAudioStreamClosed();
                clientCallback.onRecognitionFailed(
                        RECOGNITION_FAILED_AUDIO_UNAVAILABLE);
            } catch (RemoteException ignored) {
                // Ignored.
            }
@@ -192,11 +193,10 @@ public final class MusicRecognitionManagerPerUserService extends
            } finally {
                audioRecord.release();
                try {
                    mCallback.onAudioStreamClosed();
                    clientCallback.onAudioStreamClosed();
                } catch (RemoteException ignored) {
                    // Ignored.
                }

            }
        });
        // Send the pipe down to the lookup service while we write to it asynchronously.
@@ -207,13 +207,20 @@ public final class MusicRecognitionManagerPerUserService extends
     * Callback invoked by {@link android.service.musicrecognition.MusicRecognitionService} to pass
     * back the music search result.
     */
    private final class MusicRecognitionServiceCallback extends
    final class MusicRecognitionServiceCallback extends
            IMusicRecognitionServiceCallback.Stub {

        private final IMusicRecognitionManagerCallback mClientCallback;

        private MusicRecognitionServiceCallback(IMusicRecognitionManagerCallback clientCallback) {
            mClientCallback = clientCallback;
        }

        @Override
        public void onRecognitionSucceeded(MediaMetadata result, Bundle extras) {
            try {
                sanitizeBundle(extras);
                mCallback.onRecognitionSucceeded(result, extras);
                mClientCallback.onRecognitionSucceeded(result, extras);
            } catch (RemoteException ignored) {
                // Ignored.
            }
@@ -223,18 +230,23 @@ public final class MusicRecognitionManagerPerUserService extends
        @Override
        public void onRecognitionFailed(@RecognitionFailureCode int failureCode) {
            try {
                mCallback.onRecognitionFailed(failureCode);
                mClientCallback.onRecognitionFailed(failureCode);
            } catch (RemoteException ignored) {
                // Ignored.
            }
            destroyService();
        }

        private IMusicRecognitionManagerCallback getClientCallback() {
            return mClientCallback;
        }
    }

    @Override
    public void onServiceDied(@NonNull RemoteMusicRecognitionService service) {
        try {
            mCallback.onRecognitionFailed(RECOGNITION_FAILED_SERVICE_KILLED);
            service.getServerCallback().getClientCallback().onRecognitionFailed(
                    RECOGNITION_FAILED_SERVICE_KILLED);
        } catch (RemoteException e) {
            // Ignored.
        }
+58 −2
Original line number Diff line number Diff line
@@ -22,7 +22,9 @@ import static android.media.musicrecognition.MusicRecognitionManager.RECOGNITION

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.media.musicrecognition.IMusicRecognitionManager;
import android.media.musicrecognition.IMusicRecognitionManagerCallback;
import android.media.musicrecognition.RecognitionRequest;
@@ -32,7 +34,9 @@ import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.UserHandle;
import android.util.Slog;

import com.android.internal.annotations.GuardedBy;
import com.android.server.infra.AbstractMasterSystemService;
import com.android.server.infra.FrameworkResourcesServiceNameResolver;

@@ -113,9 +117,11 @@ public class MusicRecognitionManagerService extends
            enforceCaller("beginRecognition");

            synchronized (mLock) {
                int userId = UserHandle.getCallingUserId();
                final MusicRecognitionManagerPerUserService service = getServiceForUserLocked(
                        UserHandle.getCallingUserId());
                if (service != null) {
                        userId);
                if (service != null && (isDefaultServiceLocked(userId)
                        || isCalledByServiceAppLocked("beginRecognition"))) {
                    service.beginRecognitionLocked(recognitionRequest, callback);
                } else {
                    try {
@@ -139,5 +145,55 @@ public class MusicRecognitionManagerService extends
                    MusicRecognitionManagerService.this).exec(this, in, out, err, args, callback,
                    resultReceiver);
        }

        /** True if the currently set handler service is not overridden by the shell. */
        @GuardedBy("mLock")
        private boolean isDefaultServiceLocked(int userId) {
            final String defaultServiceName = mServiceNameResolver.getDefaultServiceName(userId);
            if (defaultServiceName == null) {
                return false;
            }

            final String currentServiceName = mServiceNameResolver.getServiceName(userId);
            return defaultServiceName.equals(currentServiceName);
        }

        /** True if the caller of the api is the same app which hosts the default service. */
        @GuardedBy("mLock")
        private boolean isCalledByServiceAppLocked(@NonNull String methodName) {
            final int userId = UserHandle.getCallingUserId();
            final int callingUid = Binder.getCallingUid();
            final String serviceName = mServiceNameResolver.getServiceName(userId);
            if (serviceName == null) {
                Slog.e(TAG, methodName + ": called by UID " + callingUid
                        + ", but there's no service set for user " + userId);
                return false;
            }

            final ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName);
            if (serviceComponent == null) {
                Slog.w(TAG, methodName + ": invalid service name: " + serviceName);
                return false;
            }

            final String servicePackageName = serviceComponent.getPackageName();

            final PackageManager pm = getContext().getPackageManager();
            final int serviceUid;
            try {
                serviceUid = pm.getPackageUidAsUser(servicePackageName,
                        UserHandle.getCallingUserId());
            } catch (PackageManager.NameNotFoundException e) {
                Slog.w(TAG, methodName + ": could not verify UID for " + serviceName);
                return false;
            }
            if (callingUid != serviceUid) {
                Slog.e(TAG, methodName + ": called by UID " + callingUid + ", but service UID is "
                        + serviceUid);
                return false;
            }

            return true;
        }
    }
}
+8 −3
Original line number Diff line number Diff line
@@ -21,13 +21,13 @@ import android.content.ComponentName;
import android.content.Context;
import android.media.AudioFormat;
import android.media.musicrecognition.IMusicRecognitionService;
import android.media.musicrecognition.IMusicRecognitionServiceCallback;
import android.media.musicrecognition.MusicRecognitionService;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.text.format.DateUtils;

import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
import com.android.server.musicrecognition.MusicRecognitionManagerPerUserService.MusicRecognitionServiceCallback;

/** Remote connection to an instance of {@link MusicRecognitionService}. */
public class RemoteMusicRecognitionService extends
@@ -39,11 +39,12 @@ public class RemoteMusicRecognitionService extends
    private static final long TIMEOUT_IDLE_BIND_MILLIS = 40 * DateUtils.SECOND_IN_MILLIS;

    // Allows the remote service to send back a result.
    private final IMusicRecognitionServiceCallback mServerCallback;
    private final MusicRecognitionServiceCallback
            mServerCallback;

    public RemoteMusicRecognitionService(Context context, ComponentName serviceName,
            int userId, MusicRecognitionManagerPerUserService perUserService,
            IMusicRecognitionServiceCallback callback,
            MusicRecognitionServiceCallback callback,
            boolean bindInstantServiceAllowed, boolean verbose) {
        super(context, MusicRecognitionService.ACTION_MUSIC_SEARCH_LOOKUP, serviceName, userId,
                perUserService,
@@ -66,6 +67,10 @@ public class RemoteMusicRecognitionService extends
        return TIMEOUT_IDLE_BIND_MILLIS;
    }

    MusicRecognitionServiceCallback getServerCallback() {
        return mServerCallback;
    }

    /**
     * Required, but empty since we don't need to notify the callback implementation of the request
     * results.