| /* |
| * Copyright (C) 2020 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 android.media.musicrecognition; |
| |
| import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; |
| |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.annotation.SuppressLint; |
| import android.annotation.SystemApi; |
| import android.app.Service; |
| import android.content.Intent; |
| import android.media.AudioFormat; |
| import android.media.MediaMetadata; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.os.IBinder; |
| import android.os.Looper; |
| import android.os.ParcelFileDescriptor; |
| import android.os.RemoteException; |
| import android.util.Log; |
| |
| /** |
| * Implemented by an app that wants to offer music search lookups. The system will start the |
| * service and stream up to 16 seconds of audio over the given file descriptor. |
| * |
| * @hide |
| */ |
| @SystemApi |
| public abstract class MusicRecognitionService extends Service { |
| |
| private static final String TAG = MusicRecognitionService.class.getSimpleName(); |
| |
| /** Callback for the result of the remote search. */ |
| public interface Callback { |
| /** |
| * Call this method to pass back a successful search result. |
| * |
| * @param result successful result of the search |
| * @param extras extra data to be supplied back to the caller. Note that all executable |
| * parameters and file descriptors would be removed from the supplied bundle |
| */ |
| void onRecognitionSucceeded(@NonNull MediaMetadata result, |
| @SuppressLint("NullableCollection") |
| @Nullable Bundle extras); |
| |
| /** |
| * Call this method if the search does not find a result on an error occurred. |
| */ |
| void onRecognitionFailed(@MusicRecognitionManager.RecognitionFailureCode int failureCode); |
| } |
| |
| /** |
| * Action used to start this service. |
| * |
| * @hide |
| */ |
| public static final String ACTION_MUSIC_SEARCH_LOOKUP = |
| "android.service.musicrecognition.MUSIC_RECOGNITION"; |
| |
| private Handler mHandler; |
| private final IMusicRecognitionService mServiceInterface = |
| new IMusicRecognitionService.Stub() { |
| @Override |
| public void onAudioStreamStarted(ParcelFileDescriptor fd, |
| AudioFormat audioFormat, |
| IMusicRecognitionServiceCallback callback) { |
| mHandler.sendMessage( |
| obtainMessage(MusicRecognitionService.this::onRecognize, fd, |
| audioFormat, |
| new Callback() { |
| @Override |
| public void onRecognitionSucceeded( |
| @NonNull MediaMetadata result, |
| @Nullable Bundle extras) { |
| try { |
| callback.onRecognitionSucceeded(result, extras); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| @Override |
| public void onRecognitionFailed(int failureCode) { |
| try { |
| callback.onRecognitionFailed(failureCode); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| })); |
| } |
| |
| @Override |
| public void getAttributionTag( |
| IMusicRecognitionAttributionTagCallback callback) throws RemoteException { |
| String tag = MusicRecognitionService.this.getAttributionTag(); |
| callback.onAttributionTag(tag); |
| } |
| }; |
| |
| @Override |
| public void onCreate() { |
| super.onCreate(); |
| mHandler = new Handler(Looper.getMainLooper(), null, true); |
| } |
| |
| /** |
| * Read audio from this stream. You must invoke the callback whether the music is recognized or |
| * not. |
| * |
| * @param stream containing music to be recognized. Close when you are finished. |
| * @param audioFormat describes sample rate, channels and endianness of the stream |
| * @param callback to invoke after lookup is finished. Must always be called. |
| */ |
| public abstract void onRecognize(@NonNull ParcelFileDescriptor stream, |
| @NonNull AudioFormat audioFormat, |
| @NonNull Callback callback); |
| |
| /** |
| * @hide |
| */ |
| @Nullable |
| @Override |
| public IBinder onBind(@NonNull Intent intent) { |
| if (ACTION_MUSIC_SEARCH_LOOKUP.equals(intent.getAction())) { |
| return mServiceInterface.asBinder(); |
| } |
| Log.w(TAG, |
| "Tried to bind to wrong intent (should be " + ACTION_MUSIC_SEARCH_LOOKUP + ": " |
| + intent); |
| return null; |
| } |
| } |