1 /* 2 * Copyright (C) 2021 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.policy; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.app.AppOpsManager; 22 import android.app.AppOpsManager.AttributionFlags; 23 import android.app.AppOpsManagerInternal; 24 import android.app.SyncNotedAppOp; 25 import android.app.role.RoleManager; 26 import android.content.AttributionSource; 27 import android.content.BroadcastReceiver; 28 import android.content.Context; 29 import android.content.Intent; 30 import android.content.IntentFilter; 31 import android.content.pm.PackageManager; 32 import android.content.pm.ResolveInfo; 33 import android.location.LocationManagerInternal; 34 import android.net.Uri; 35 import android.os.Binder; 36 import android.os.Bundle; 37 import android.os.IBinder; 38 import android.os.PackageTagsList; 39 import android.os.Process; 40 import android.os.SystemProperties; 41 import android.os.UserHandle; 42 import android.service.voice.VoiceInteractionManagerInternal; 43 import android.service.voice.VoiceInteractionManagerInternal.HotwordDetectionServiceIdentity; 44 import android.text.TextUtils; 45 import android.util.Log; 46 import android.util.SparseArray; 47 48 import com.android.internal.annotations.GuardedBy; 49 import com.android.internal.util.function.HeptFunction; 50 import com.android.internal.util.function.HexFunction; 51 import com.android.internal.util.function.QuadFunction; 52 import com.android.internal.util.function.QuintConsumer; 53 import com.android.internal.util.function.QuintFunction; 54 import com.android.internal.util.function.UndecFunction; 55 import com.android.server.LocalServices; 56 57 import java.io.PrintWriter; 58 import java.util.Arrays; 59 import java.util.List; 60 import java.util.Map; 61 import java.util.concurrent.ConcurrentHashMap; 62 63 /** 64 * This class defines policy for special behaviors around app ops. 65 */ 66 public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegate { 67 private static final String LOG_TAG = AppOpsPolicy.class.getName(); 68 69 private static final String ACTIVITY_RECOGNITION_TAGS = 70 "android:activity_recognition_allow_listed_tags"; 71 private static final String ACTIVITY_RECOGNITION_TAGS_SEPARATOR = ";"; 72 private static final boolean SYSPROP_HOTWORD_DETECTION_SERVICE_REQUIRED = 73 SystemProperties.getBoolean("ro.hotword.detection_service_required", false); 74 75 @NonNull 76 private final Object mLock = new Object(); 77 78 @NonNull 79 private final IBinder mToken = new Binder(); 80 81 @NonNull 82 private final Context mContext; 83 84 @NonNull 85 private final RoleManager mRoleManager; 86 87 @NonNull 88 private final VoiceInteractionManagerInternal mVoiceInteractionManagerInternal; 89 90 /** 91 * Whether this device allows only the HotwordDetectionService to use 92 * OP_RECORD_AUDIO_HOTWORD which doesn't incur the privacy indicator. 93 */ 94 private final boolean mIsHotwordDetectionServiceRequired; 95 96 /** 97 * The locking policy around the location tags is a bit special. Since we want to 98 * avoid grabbing the lock on every op note we are taking the approach where the 99 * read and write are being done via a thread-safe data structure such that the 100 * lookup/insert are single thread-safe calls. When we update the cached state we 101 * use a lock to ensure the update's lookup and store calls are done atomically, 102 * so multiple writers would not interleave. The tradeoff is we make is that the 103 * concurrent data structure would use boxing/unboxing of integers but this is 104 * preferred to locking. 105 */ 106 @GuardedBy("mLock - writes only - see above") 107 @NonNull 108 private final ConcurrentHashMap<Integer, PackageTagsList> mLocationTags = 109 new ConcurrentHashMap<>(); 110 111 // location tags can vary per uid - but we merge all tags under an app id into the final data 112 // structure above 113 @GuardedBy("mLock") 114 private final SparseArray<PackageTagsList> mPerUidLocationTags = new SparseArray<>(); 115 116 // activity recognition currently only grabs tags from the APK manifest. we know that the 117 // manifest is the same for all users, so there's no need to track variations in tags across 118 // different users. if that logic ever changes, this might need to behave more like location 119 // tags above. 120 @GuardedBy("mLock - writes only - see above") 121 @NonNull 122 private final ConcurrentHashMap<Integer, PackageTagsList> mActivityRecognitionTags = 123 new ConcurrentHashMap<>(); 124 AppOpsPolicy(@onNull Context context)125 public AppOpsPolicy(@NonNull Context context) { 126 mContext = context; 127 mRoleManager = mContext.getSystemService(RoleManager.class); 128 mVoiceInteractionManagerInternal = LocalServices.getService( 129 VoiceInteractionManagerInternal.class); 130 mIsHotwordDetectionServiceRequired = isHotwordDetectionServiceRequired( 131 mContext.getPackageManager()); 132 133 final LocationManagerInternal locationManagerInternal = LocalServices.getService( 134 LocationManagerInternal.class); 135 locationManagerInternal.setLocationPackageTagsListener( 136 (uid, packageTagsList) -> { 137 synchronized (mLock) { 138 if (packageTagsList.isEmpty()) { 139 mPerUidLocationTags.remove(uid); 140 } else { 141 mPerUidLocationTags.set(uid, packageTagsList); 142 } 143 144 int appId = UserHandle.getAppId(uid); 145 PackageTagsList.Builder appIdTags = new PackageTagsList.Builder(1); 146 int size = mPerUidLocationTags.size(); 147 for (int i = 0; i < size; i++) { 148 if (UserHandle.getAppId(mPerUidLocationTags.keyAt(i)) == appId) { 149 appIdTags.add(mPerUidLocationTags.valueAt(i)); 150 } 151 } 152 153 updateAllowListedTagsForPackageLocked(appId, appIdTags.build(), 154 mLocationTags); 155 } 156 }); 157 158 final IntentFilter intentFilter = new IntentFilter(); 159 intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED); 160 intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); 161 intentFilter.addDataScheme("package"); 162 163 context.registerReceiverAsUser(new BroadcastReceiver() { 164 @Override 165 public void onReceive(Context context, Intent intent) { 166 final Uri uri = intent.getData(); 167 if (uri == null) { 168 return; 169 } 170 final String packageName = uri.getSchemeSpecificPart(); 171 if (TextUtils.isEmpty(packageName)) { 172 return; 173 } 174 final List<String> activityRecognizers = mRoleManager.getRoleHolders( 175 RoleManager.ROLE_SYSTEM_ACTIVITY_RECOGNIZER); 176 if (activityRecognizers.contains(packageName)) { 177 updateActivityRecognizerTags(packageName); 178 } 179 } 180 }, UserHandle.SYSTEM, intentFilter, null, null); 181 182 mRoleManager.addOnRoleHoldersChangedListenerAsUser(context.getMainExecutor(), 183 (String roleName, UserHandle user) -> { 184 if (RoleManager.ROLE_SYSTEM_ACTIVITY_RECOGNIZER.equals(roleName)) { 185 initializeActivityRecognizersTags(); 186 } 187 }, UserHandle.SYSTEM); 188 189 initializeActivityRecognizersTags(); 190 191 // Restrict phone call ops if the TelecomService will not start (conditioned on having 192 // FEATURE_MICROPHONE, FEATURE_TELECOM, or FEATURE_TELEPHONY). 193 PackageManager pm = mContext.getPackageManager(); 194 if (!pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY) 195 && !pm.hasSystemFeature(PackageManager.FEATURE_MICROPHONE) 196 && !pm.hasSystemFeature(PackageManager.FEATURE_TELECOM)) { 197 AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class); 198 appOps.setUserRestrictionForUser(AppOpsManager.OP_PHONE_CALL_MICROPHONE, true, mToken, 199 null, UserHandle.USER_ALL); 200 appOps.setUserRestrictionForUser(AppOpsManager.OP_PHONE_CALL_CAMERA, true, mToken, 201 null, UserHandle.USER_ALL); 202 } 203 } 204 205 /** 206 * @hide 207 */ isHotwordDetectionServiceRequired(PackageManager pm)208 public static boolean isHotwordDetectionServiceRequired(PackageManager pm) { 209 // The HotwordDetectionService APIs aren't ready yet for Auto or TV. 210 if (pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE) 211 || pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) { 212 return false; 213 } 214 return SYSPROP_HOTWORD_DETECTION_SERVICE_REQUIRED; 215 } 216 217 @Override checkOperation(int code, int uid, String packageName, @Nullable String attributionTag, boolean raw, QuintFunction<Integer, Integer, String, String, Boolean, Integer> superImpl)218 public int checkOperation(int code, int uid, String packageName, 219 @Nullable String attributionTag, boolean raw, 220 QuintFunction<Integer, Integer, String, String, Boolean, Integer> superImpl) { 221 return superImpl.apply(code, resolveUid(code, uid), packageName, attributionTag, raw); 222 } 223 224 @Override checkAudioOperation(int code, int usage, int uid, String packageName, QuadFunction<Integer, Integer, Integer, String, Integer> superImpl)225 public int checkAudioOperation(int code, int usage, int uid, String packageName, 226 QuadFunction<Integer, Integer, Integer, String, Integer> superImpl) { 227 return superImpl.apply(code, usage, uid, packageName); 228 } 229 230 @Override noteOperation(int code, int uid, @Nullable String packageName, @Nullable String attributionTag, boolean shouldCollectAsyncNotedOp, @Nullable String message, boolean shouldCollectMessage, @NonNull HeptFunction<Integer, Integer, String, String, Boolean, String, Boolean, SyncNotedAppOp> superImpl)231 public SyncNotedAppOp noteOperation(int code, int uid, @Nullable String packageName, 232 @Nullable String attributionTag, boolean shouldCollectAsyncNotedOp, @Nullable 233 String message, boolean shouldCollectMessage, @NonNull HeptFunction<Integer, Integer, 234 String, String, Boolean, String, Boolean, SyncNotedAppOp> superImpl) { 235 return superImpl.apply(resolveDatasourceOp(code, uid, packageName, attributionTag), 236 resolveUid(code, uid), packageName, attributionTag, shouldCollectAsyncNotedOp, 237 message, shouldCollectMessage); 238 } 239 240 @Override noteProxyOperation(int code, @NonNull AttributionSource attributionSource, boolean shouldCollectAsyncNotedOp, @Nullable String message, boolean shouldCollectMessage, boolean skipProxyOperation, @NonNull HexFunction<Integer, AttributionSource, Boolean, String, Boolean, Boolean, SyncNotedAppOp> superImpl)241 public SyncNotedAppOp noteProxyOperation(int code, @NonNull AttributionSource attributionSource, 242 boolean shouldCollectAsyncNotedOp, @Nullable String message, 243 boolean shouldCollectMessage, boolean skipProxyOperation, @NonNull HexFunction<Integer, 244 AttributionSource, Boolean, String, Boolean, Boolean, 245 SyncNotedAppOp> superImpl) { 246 return superImpl.apply(resolveDatasourceOp(code, attributionSource.getUid(), 247 attributionSource.getPackageName(), attributionSource.getAttributionTag()), 248 attributionSource, shouldCollectAsyncNotedOp, message, shouldCollectMessage, 249 skipProxyOperation); 250 } 251 252 @Override startOperation(IBinder token, int code, int uid, @Nullable String packageName, @Nullable String attributionTag, boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage, @AttributionFlags int attributionFlags, int attributionChainId, @NonNull UndecFunction<IBinder, Integer, Integer, String, String, Boolean, Boolean, String, Boolean, Integer, Integer, SyncNotedAppOp> superImpl)253 public SyncNotedAppOp startOperation(IBinder token, int code, int uid, 254 @Nullable String packageName, @Nullable String attributionTag, 255 boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, String message, 256 boolean shouldCollectMessage, @AttributionFlags int attributionFlags, 257 int attributionChainId, @NonNull UndecFunction<IBinder, Integer, Integer, String, 258 String, Boolean, Boolean, String, Boolean, Integer, Integer, 259 SyncNotedAppOp> superImpl) { 260 return superImpl.apply(token, resolveDatasourceOp(code, uid, packageName, attributionTag), 261 resolveUid(code, uid), packageName, attributionTag, startIfModeDefault, 262 shouldCollectAsyncNotedOp, message, shouldCollectMessage, attributionFlags, 263 attributionChainId); 264 } 265 266 @Override startProxyOperation(@onNull IBinder clientId, int code, @NonNull AttributionSource attributionSource, boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage, boolean skipProxyOperation, @AttributionFlags int proxyAttributionFlags, @AttributionFlags int proxiedAttributionFlags, int attributionChainId, @NonNull UndecFunction<IBinder, Integer, AttributionSource, Boolean, Boolean, String, Boolean, Boolean, Integer, Integer, Integer, SyncNotedAppOp> superImpl)267 public SyncNotedAppOp startProxyOperation(@NonNull IBinder clientId, int code, 268 @NonNull AttributionSource attributionSource, boolean startIfModeDefault, 269 boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage, 270 boolean skipProxyOperation, @AttributionFlags int proxyAttributionFlags, 271 @AttributionFlags int proxiedAttributionFlags, int attributionChainId, 272 @NonNull UndecFunction<IBinder, Integer, AttributionSource, Boolean, Boolean, String, 273 Boolean, Boolean, Integer, Integer, Integer, SyncNotedAppOp> superImpl) { 274 return superImpl.apply(clientId, resolveDatasourceOp(code, attributionSource.getUid(), 275 attributionSource.getPackageName(), attributionSource.getAttributionTag()), 276 attributionSource, startIfModeDefault, shouldCollectAsyncNotedOp, message, 277 shouldCollectMessage, skipProxyOperation, proxyAttributionFlags, 278 proxiedAttributionFlags, attributionChainId); 279 } 280 281 @Override finishOperation(IBinder clientId, int code, int uid, String packageName, String attributionTag, @NonNull QuintConsumer<IBinder, Integer, Integer, String, String> superImpl)282 public void finishOperation(IBinder clientId, int code, int uid, String packageName, 283 String attributionTag, 284 @NonNull QuintConsumer<IBinder, Integer, Integer, String, String> superImpl) { 285 superImpl.accept(clientId, resolveDatasourceOp(code, uid, packageName, attributionTag), 286 resolveUid(code, uid), packageName, attributionTag); 287 } 288 289 @Override finishProxyOperation(@onNull IBinder clientId, int code, @NonNull AttributionSource attributionSource, boolean skipProxyOperation, @NonNull QuadFunction<IBinder, Integer, AttributionSource, Boolean, Void> superImpl)290 public void finishProxyOperation(@NonNull IBinder clientId, int code, 291 @NonNull AttributionSource attributionSource, boolean skipProxyOperation, 292 @NonNull QuadFunction<IBinder, Integer, AttributionSource, Boolean, Void> superImpl) { 293 superImpl.apply(clientId, resolveDatasourceOp(code, attributionSource.getUid(), 294 attributionSource.getPackageName(), attributionSource.getAttributionTag()), 295 attributionSource, skipProxyOperation); 296 } 297 298 /** 299 * Write location and activity recognition tags to console. 300 * See also {@code adb shell dumpsys appops}. 301 */ dumpTags(PrintWriter writer)302 public void dumpTags(PrintWriter writer) { 303 if (!mLocationTags.isEmpty()) { 304 writer.println(" AppOps policy location tags:"); 305 writeTags(mLocationTags, writer); 306 writer.println(); 307 } 308 if (!mActivityRecognitionTags.isEmpty()) { 309 writer.println(" AppOps policy activity recognition tags:"); 310 writeTags(mActivityRecognitionTags, writer); 311 writer.println(); 312 } 313 } 314 writeTags(Map<Integer, PackageTagsList> tags, PrintWriter writer)315 private void writeTags(Map<Integer, PackageTagsList> tags, PrintWriter writer) { 316 int counter = 0; 317 for (Map.Entry<Integer, PackageTagsList> tagEntry : tags.entrySet()) { 318 writer.print(" #"); writer.print(counter++); writer.print(": "); 319 writer.print(tagEntry.getKey().toString()); writer.print("="); 320 tagEntry.getValue().dump(writer); 321 } 322 } 323 324 resolveDatasourceOp(int code, int uid, @NonNull String packageName, @Nullable String attributionTag)325 private int resolveDatasourceOp(int code, int uid, @NonNull String packageName, 326 @Nullable String attributionTag) { 327 code = resolveRecordAudioOp(code, uid); 328 code = resolveSandboxedServiceOp(code, uid); 329 if (attributionTag == null) { 330 return code; 331 } 332 int resolvedCode = resolveLocationOp(code); 333 if (resolvedCode != code) { 334 if (isDatasourceAttributionTag(uid, packageName, attributionTag, 335 mLocationTags)) { 336 return resolvedCode; 337 } 338 } else { 339 resolvedCode = resolveArOp(code); 340 if (resolvedCode != code) { 341 if (isDatasourceAttributionTag(uid, packageName, attributionTag, 342 mActivityRecognitionTags)) { 343 return resolvedCode; 344 } 345 } 346 } 347 return code; 348 } 349 initializeActivityRecognizersTags()350 private void initializeActivityRecognizersTags() { 351 final List<String> activityRecognizers = mRoleManager.getRoleHolders( 352 RoleManager.ROLE_SYSTEM_ACTIVITY_RECOGNIZER); 353 final int recognizerCount = activityRecognizers.size(); 354 if (recognizerCount > 0) { 355 for (int i = 0; i < recognizerCount; i++) { 356 final String activityRecognizer = activityRecognizers.get(i); 357 updateActivityRecognizerTags(activityRecognizer); 358 } 359 } else { 360 clearActivityRecognitionTags(); 361 } 362 } 363 clearActivityRecognitionTags()364 private void clearActivityRecognitionTags() { 365 synchronized (mLock) { 366 mActivityRecognitionTags.clear(); 367 } 368 } 369 updateActivityRecognizerTags(@onNull String activityRecognizer)370 private void updateActivityRecognizerTags(@NonNull String activityRecognizer) { 371 final int flags = PackageManager.GET_SERVICES 372 | PackageManager.GET_META_DATA 373 | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS 374 | PackageManager.MATCH_DIRECT_BOOT_AWARE 375 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE; 376 377 final Intent intent = new Intent(Intent.ACTION_ACTIVITY_RECOGNIZER); 378 intent.setPackage(activityRecognizer); 379 final ResolveInfo resolvedService = mContext.getPackageManager() 380 .resolveServiceAsUser(intent, flags, UserHandle.USER_SYSTEM); 381 if (resolvedService == null || resolvedService.serviceInfo == null) { 382 Log.w(LOG_TAG, "Service recognizer doesn't handle " 383 + Intent.ACTION_ACTIVITY_RECOGNIZER + ", ignoring!"); 384 return; 385 } 386 final Bundle metaData = resolvedService.serviceInfo.metaData; 387 if (metaData == null) { 388 return; 389 } 390 final String tagsList = metaData.getString(ACTIVITY_RECOGNITION_TAGS); 391 if (!TextUtils.isEmpty(tagsList)) { 392 PackageTagsList packageTagsList = new PackageTagsList.Builder(1).add( 393 resolvedService.serviceInfo.packageName, 394 Arrays.asList(tagsList.split(ACTIVITY_RECOGNITION_TAGS_SEPARATOR))).build(); 395 synchronized (mLock) { 396 updateAllowListedTagsForPackageLocked( 397 UserHandle.getAppId(resolvedService.serviceInfo.applicationInfo.uid), 398 packageTagsList, 399 mActivityRecognitionTags); 400 } 401 } 402 } 403 updateAllowListedTagsForPackageLocked(int appId, PackageTagsList packageTagsList, ConcurrentHashMap<Integer, PackageTagsList> datastore)404 private static void updateAllowListedTagsForPackageLocked(int appId, 405 PackageTagsList packageTagsList, 406 ConcurrentHashMap<Integer, PackageTagsList> datastore) { 407 datastore.put(appId, packageTagsList); 408 } 409 isDatasourceAttributionTag(int uid, @NonNull String packageName, @NonNull String attributionTag, @NonNull Map<Integer, PackageTagsList> mappedOps)410 private static boolean isDatasourceAttributionTag(int uid, @NonNull String packageName, 411 @NonNull String attributionTag, @NonNull Map<Integer, PackageTagsList> mappedOps) { 412 // Only a single lookup from the underlying concurrent data structure 413 final PackageTagsList appIdTags = mappedOps.get(UserHandle.getAppId(uid)); 414 return appIdTags != null && appIdTags.contains(packageName, attributionTag); 415 } 416 resolveLocationOp(int code)417 private static int resolveLocationOp(int code) { 418 switch (code) { 419 case AppOpsManager.OP_FINE_LOCATION: 420 return AppOpsManager.OP_FINE_LOCATION_SOURCE; 421 case AppOpsManager.OP_COARSE_LOCATION: 422 return AppOpsManager.OP_COARSE_LOCATION_SOURCE; 423 } 424 return code; 425 } 426 resolveArOp(int code)427 private static int resolveArOp(int code) { 428 if (code == AppOpsManager.OP_ACTIVITY_RECOGNITION) { 429 return AppOpsManager.OP_ACTIVITY_RECOGNITION_SOURCE; 430 } 431 return code; 432 } 433 resolveRecordAudioOp(int code, int uid)434 private int resolveRecordAudioOp(int code, int uid) { 435 if (code == AppOpsManager.OP_RECORD_AUDIO_HOTWORD) { 436 if (!mIsHotwordDetectionServiceRequired) { 437 return code; 438 } 439 // Only the HotwordDetectionService can use the RECORD_AUDIO_HOTWORD op which doesn't 440 // incur the privacy indicator. Downgrade to standard RECORD_AUDIO for other processes. 441 final HotwordDetectionServiceIdentity hotwordDetectionServiceIdentity = 442 mVoiceInteractionManagerInternal.getHotwordDetectionServiceIdentity(); 443 if (hotwordDetectionServiceIdentity != null 444 && uid == hotwordDetectionServiceIdentity.getIsolatedUid()) { 445 return code; 446 } 447 return AppOpsManager.OP_RECORD_AUDIO; 448 } 449 return code; 450 } 451 resolveSandboxedServiceOp(int code, int uid)452 private int resolveSandboxedServiceOp(int code, int uid) { 453 if (!Process.isIsolated(uid) // simple check which fails-fast for the common case 454 || !(code == AppOpsManager.OP_RECORD_AUDIO || code == AppOpsManager.OP_CAMERA)) { 455 return code; 456 } 457 final HotwordDetectionServiceIdentity hotwordDetectionServiceIdentity = 458 mVoiceInteractionManagerInternal.getHotwordDetectionServiceIdentity(); 459 if (hotwordDetectionServiceIdentity != null 460 && uid == hotwordDetectionServiceIdentity.getIsolatedUid()) { 461 // Upgrade the op such that no indicators is shown for camera or audio service. This 462 // will bypass the permission checking for the original OP_RECORD_AUDIO and OP_CAMERA. 463 switch (code) { 464 case AppOpsManager.OP_RECORD_AUDIO: 465 return AppOpsManager.OP_RECORD_AUDIO_SANDBOXED; 466 case AppOpsManager.OP_CAMERA: 467 return AppOpsManager.OP_CAMERA_SANDBOXED; 468 } 469 } 470 return code; 471 } 472 473 resolveUid(int code, int uid)474 private int resolveUid(int code, int uid) { 475 // The HotwordDetectionService is an isolated service, which ordinarily cannot hold 476 // permissions. So we allow it to assume the owning package identity for certain 477 // operations. 478 // Note: The package name coming from the audio server is already the one for the owning 479 // package, so we don't need to modify it. 480 if (Process.isIsolated(uid) // simple check which fails-fast for the common case 481 && (code == AppOpsManager.OP_RECORD_AUDIO 482 || code == AppOpsManager.OP_RECORD_AUDIO_HOTWORD 483 || code == AppOpsManager.OP_CAMERA)) { 484 final HotwordDetectionServiceIdentity hotwordDetectionServiceIdentity = 485 mVoiceInteractionManagerInternal.getHotwordDetectionServiceIdentity(); 486 if (hotwordDetectionServiceIdentity != null 487 && uid == hotwordDetectionServiceIdentity.getIsolatedUid()) { 488 uid = hotwordDetectionServiceIdentity.getOwnerUid(); 489 } 490 } 491 return uid; 492 } 493 } 494