• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.musicrecognition;
18 
19 import static android.Manifest.permission.MANAGE_MUSIC_RECOGNITION;
20 import static android.content.PermissionChecker.PERMISSION_GRANTED;
21 import static android.media.musicrecognition.MusicRecognitionManager.RECOGNITION_FAILED_SERVICE_UNAVAILABLE;
22 
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.content.ComponentName;
26 import android.content.Context;
27 import android.content.pm.PackageManager;
28 import android.media.musicrecognition.IMusicRecognitionManager;
29 import android.media.musicrecognition.IMusicRecognitionManagerCallback;
30 import android.media.musicrecognition.RecognitionRequest;
31 import android.os.Binder;
32 import android.os.IBinder;
33 import android.os.RemoteException;
34 import android.os.ResultReceiver;
35 import android.os.ShellCallback;
36 import android.os.UserHandle;
37 import android.util.Slog;
38 
39 import com.android.internal.annotations.GuardedBy;
40 import com.android.server.infra.AbstractMasterSystemService;
41 import com.android.server.infra.FrameworkResourcesServiceNameResolver;
42 
43 import java.io.FileDescriptor;
44 import java.util.concurrent.ExecutorService;
45 import java.util.concurrent.Executors;
46 
47 /**
48  * Service which allows audio to be securely streamed to a designated {@link
49  * MusicRecognitionService}.
50  */
51 public class MusicRecognitionManagerService extends
52         AbstractMasterSystemService<MusicRecognitionManagerService,
53                 MusicRecognitionManagerPerUserService> {
54 
55     private static final String TAG = MusicRecognitionManagerService.class.getSimpleName();
56     private static final int MAX_TEMP_SERVICE_SUBSTITUTION_DURATION_MS = 60_000;
57 
58     private MusicRecognitionManagerStub mMusicRecognitionManagerStub;
59     final ExecutorService mExecutorService = Executors.newCachedThreadPool();
60 
61     /**
62      * Initializes the system service.
63      *
64      * Subclasses must define a single argument constructor that accepts the context
65      * and passes it to super.
66      *
67      * @param context The system server context.
68      */
MusicRecognitionManagerService(@onNull Context context)69     public MusicRecognitionManagerService(@NonNull Context context) {
70         super(context, new FrameworkResourcesServiceNameResolver(context,
71                         com.android.internal.R.string.config_defaultMusicRecognitionService),
72                 /** disallowProperty */null);
73     }
74 
75     @Nullable
76     @Override
newServiceLocked(int resolvedUserId, boolean disabled)77     protected MusicRecognitionManagerPerUserService newServiceLocked(int resolvedUserId,
78             boolean disabled) {
79         return new MusicRecognitionManagerPerUserService(this, mLock, resolvedUserId);
80     }
81 
82     @Override
onStart()83     public void onStart() {
84         mMusicRecognitionManagerStub = new MusicRecognitionManagerStub();
85         publishBinderService(Context.MUSIC_RECOGNITION_SERVICE, mMusicRecognitionManagerStub);
86     }
87 
enforceCaller(String func)88     private void enforceCaller(String func) {
89         Context ctx = getContext();
90         if (ctx.checkCallingPermission(android.Manifest.permission.MANAGE_MUSIC_RECOGNITION)
91                 == PERMISSION_GRANTED) {
92             return;
93         }
94 
95         String msg = "Permission Denial: " + func + " from pid="
96                 + Binder.getCallingPid()
97                 + ", uid=" + Binder.getCallingUid()
98                 + " doesn't hold " + android.Manifest.permission.MANAGE_MUSIC_RECOGNITION;
99         throw new SecurityException(msg);
100     }
101 
102     @Override
enforceCallingPermissionForManagement()103     protected void enforceCallingPermissionForManagement() {
104         getContext().enforceCallingPermission(MANAGE_MUSIC_RECOGNITION, TAG);
105     }
106 
107     @Override
getMaximumTemporaryServiceDurationMs()108     protected int getMaximumTemporaryServiceDurationMs() {
109         return MAX_TEMP_SERVICE_SUBSTITUTION_DURATION_MS;
110     }
111 
112     final class MusicRecognitionManagerStub extends IMusicRecognitionManager.Stub {
113         @Override
beginRecognition( @onNull RecognitionRequest recognitionRequest, @NonNull IBinder callback)114         public void beginRecognition(
115                 @NonNull RecognitionRequest recognitionRequest,
116                 @NonNull IBinder callback) {
117             enforceCaller("beginRecognition");
118 
119             synchronized (mLock) {
120                 int userId = UserHandle.getCallingUserId();
121                 final MusicRecognitionManagerPerUserService service = getServiceForUserLocked(
122                         userId);
123                 if (service != null && (isDefaultServiceLocked(userId)
124                         || isCalledByServiceAppLocked("beginRecognition"))) {
125                     service.beginRecognitionLocked(recognitionRequest, callback);
126                 } else {
127                     try {
128                         IMusicRecognitionManagerCallback.Stub.asInterface(callback)
129                                 .onRecognitionFailed(RECOGNITION_FAILED_SERVICE_UNAVAILABLE);
130                     } catch (RemoteException e) {
131                         // ignored.
132                     }
133                 }
134             }
135         }
136 
137         @Override
onShellCommand(@ullable FileDescriptor in, @Nullable FileDescriptor out, @Nullable FileDescriptor err, @NonNull String[] args, @Nullable ShellCallback callback, @NonNull ResultReceiver resultReceiver)138         public void onShellCommand(@Nullable FileDescriptor in,
139                 @Nullable FileDescriptor out,
140                 @Nullable FileDescriptor err,
141                 @NonNull String[] args,
142                 @Nullable ShellCallback callback,
143                 @NonNull ResultReceiver resultReceiver) throws RemoteException {
144             new MusicRecognitionManagerServiceShellCommand(
145                     MusicRecognitionManagerService.this).exec(this, in, out, err, args, callback,
146                     resultReceiver);
147         }
148 
149         /** True if the currently set handler service is not overridden by the shell. */
150         @GuardedBy("mLock")
isDefaultServiceLocked(int userId)151         private boolean isDefaultServiceLocked(int userId) {
152             final String defaultServiceName = mServiceNameResolver.getDefaultServiceName(userId);
153             if (defaultServiceName == null) {
154                 return false;
155             }
156 
157             final String currentServiceName = mServiceNameResolver.getServiceName(userId);
158             return defaultServiceName.equals(currentServiceName);
159         }
160 
161         /** True if the caller of the api is the same app which hosts the default service. */
162         @GuardedBy("mLock")
isCalledByServiceAppLocked(@onNull String methodName)163         private boolean isCalledByServiceAppLocked(@NonNull String methodName) {
164             final int userId = UserHandle.getCallingUserId();
165             final int callingUid = Binder.getCallingUid();
166             final String serviceName = mServiceNameResolver.getServiceName(userId);
167             if (serviceName == null) {
168                 Slog.e(TAG, methodName + ": called by UID " + callingUid
169                         + ", but there's no service set for user " + userId);
170                 return false;
171             }
172 
173             final ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName);
174             if (serviceComponent == null) {
175                 Slog.w(TAG, methodName + ": invalid service name: " + serviceName);
176                 return false;
177             }
178 
179             final String servicePackageName = serviceComponent.getPackageName();
180 
181             final PackageManager pm = getContext().getPackageManager();
182             final int serviceUid;
183             try {
184                 serviceUid = pm.getPackageUidAsUser(servicePackageName,
185                         UserHandle.getCallingUserId());
186             } catch (PackageManager.NameNotFoundException e) {
187                 Slog.w(TAG, methodName + ": could not verify UID for " + serviceName);
188                 return false;
189             }
190             if (callingUid != serviceUid) {
191                 Slog.e(TAG, methodName + ": called by UID " + callingUid + ", but service UID is "
192                         + serviceUid);
193                 return false;
194             }
195 
196             return true;
197         }
198     }
199 }
200