From 5fc6910227c9052328002cedbf211ecb5edfcc4b Mon Sep 17 00:00:00 2001 From: Ahaan Ugale Date: Mon, 7 Jun 2021 11:36:09 -0700 Subject: [PATCH] Grant HotwordDetectionService RECORD_AUDIO and CAPTURE_AUDIO_HOTWORD. HotwordDetectionService is an isolated service which ordinarily cannot hold permissions. An isolated service has its own uid separate from the owning package. To allow it to access mic audio, we dynamically override the permissions check based on the currently bound HotwordDetectionService. Bug: 190011174 Test: manual - sample app can read audio (with a few other wip changes) Test: atest CtsVoiceInteractionTestCases Change-Id: I5abc809546184ef13cb9eb009f916afae6bdf1af --- .../VoiceInteractionManagerInternal.java | 37 ++++++++ .../permission/PermissionManagerService.java | 92 ++++++++++++++----- .../PermissionManagerServiceInternal.java | 24 +++++ .../android/server/policy/AppOpsPolicy.java | 38 +++++++- .../HotwordDetectionConnection.java | 25 ++++- .../VoiceInteractionManagerService.java | 22 ++++- .../VoiceInteractionManagerServiceImpl.java | 6 +- 7 files changed, 207 insertions(+), 37 deletions(-) diff --git a/core/java/android/service/voice/VoiceInteractionManagerInternal.java b/core/java/android/service/voice/VoiceInteractionManagerInternal.java index f5c959137d9b7..c048286545c34 100644 --- a/core/java/android/service/voice/VoiceInteractionManagerInternal.java +++ b/core/java/android/service/voice/VoiceInteractionManagerInternal.java @@ -16,9 +16,12 @@ package android.service.voice; +import android.annotation.Nullable; import android.os.Bundle; import android.os.IBinder; +import com.android.internal.annotations.Immutable; + /** * @hide @@ -46,4 +49,38 @@ public abstract class VoiceInteractionManagerInternal { * Returns whether the given package is currently in an active session */ public abstract boolean hasActiveSession(String packageName); + + /** + * Gets the identity of the currently active HotwordDetectionService. + * + * @see HotwordDetectionServiceIdentity + */ + @Nullable + public abstract HotwordDetectionServiceIdentity getHotwordDetectionServiceIdentity(); + + /** + * Provides the uids of the currently active + * {@link android.service.voice.HotwordDetectionService} and its owning package. The + * HotwordDetectionService is an isolated service, so it has a separate uid. + */ + @Immutable + public static class HotwordDetectionServiceIdentity { + private final int mIsolatedUid; + private final int mOwnerUid; + + public HotwordDetectionServiceIdentity(int isolatedUid, int ownerUid) { + mIsolatedUid = isolatedUid; + mOwnerUid = ownerUid; + } + + /** Gets the uid of the currently active isolated process hosting the service. */ + public int getIsolatedUid() { + return mIsolatedUid; + } + + /** Gets the uid of the package that provides the HotwordDetectionService. */ + public int getOwnerUid() { + return mOwnerUid; + } + } } \ No newline at end of file diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index b0f8ee1925c57..cd78bf95c3363 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -17,7 +17,9 @@ package com.android.server.pm.permission; import static android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY; +import static android.Manifest.permission.CAPTURE_AUDIO_HOTWORD; import static android.Manifest.permission.READ_EXTERNAL_STORAGE; +import static android.Manifest.permission.RECORD_AUDIO; import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; import static android.app.AppOpsManager.MODE_ALLOWED; import static android.app.AppOpsManager.MODE_ERRORED; @@ -148,6 +150,7 @@ import com.android.server.pm.UserManagerInternal; import com.android.server.pm.UserManagerService; import com.android.server.pm.parsing.PackageInfoUtils; import com.android.server.pm.parsing.pkg.AndroidPackage; +import com.android.server.pm.permission.PermissionManagerServiceInternal.HotwordDetectionServiceProvider; import com.android.server.pm.permission.PermissionManagerServiceInternal.OnRuntimePermissionStateChangedListener; import com.android.server.policy.PermissionPolicyInternal; import com.android.server.policy.SoftRestrictedPermissionPolicy; @@ -308,6 +311,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { @NonNull private final OnPermissionChangeListeners mOnPermissionChangeListeners; + @Nullable + private HotwordDetectionServiceProvider mHotwordDetectionServiceProvider; + // TODO: Take a look at the methods defined in the callback. // The callback was initially created to support the split between permission // manager and the package manager. However, it's started to be used for other @@ -5200,6 +5206,16 @@ public class PermissionManagerService extends IPermissionManager.Stub { public int[] getGidsForUid(int uid) { return PermissionManagerService.this.getGidsForUid(uid); } + + @Override + public void setHotwordDetectionServiceProvider(HotwordDetectionServiceProvider provider) { + mHotwordDetectionServiceProvider = provider; + } + + @Override + public HotwordDetectionServiceProvider getHotwordDetectionServiceProvider() { + return mHotwordDetectionServiceProvider; + } } /** @@ -5476,10 +5492,13 @@ public class PermissionManagerService extends IPermissionManager.Stub { private final @NonNull Context mContext; private final @NonNull AppOpsManager mAppOpsManager; + private final @NonNull PermissionManagerServiceInternal mPermissionManagerServiceInternal; PermissionCheckerService(@NonNull Context context) { mContext = context; mAppOpsManager = mContext.getSystemService(AppOpsManager.class); + mPermissionManagerServiceInternal = + LocalServices.getService(PermissionManagerServiceInternal.class); } @Override @@ -5492,8 +5511,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { Objects.requireNonNull(attributionSourceState); final AttributionSource attributionSource = new AttributionSource( attributionSourceState); - final int result = checkPermission(mContext, permission, attributionSource, message, - forDataDelivery, startDataDelivery, fromDatasource, attributedOp); + final int result = checkPermission(mContext, mPermissionManagerServiceInternal, + permission, attributionSource, message, forDataDelivery, startDataDelivery, + fromDatasource, attributedOp); // Finish any started op if some step in the attribution chain failed. if (startDataDelivery && result != PermissionChecker.PERMISSION_GRANTED) { if (attributedOp == AppOpsManager.OP_NONE) { @@ -5581,10 +5601,11 @@ public class PermissionManagerService extends IPermissionManager.Stub { } @PermissionCheckerManager.PermissionResult - private static int checkPermission(@NonNull Context context, @NonNull String permission, - @NonNull AttributionSource attributionSource, @Nullable String message, - boolean forDataDelivery, boolean startDataDelivery, boolean fromDatasource, - int attributedOp) { + private static int checkPermission(@NonNull Context context, + @NonNull PermissionManagerServiceInternal permissionManagerServiceInt, + @NonNull String permission, @NonNull AttributionSource attributionSource, + @Nullable String message, boolean forDataDelivery, boolean startDataDelivery, + boolean fromDatasource, int attributedOp) { PermissionInfo permissionInfo = sPlatformPermissions.get(permission); if (permissionInfo == null) { @@ -5601,22 +5622,25 @@ public class PermissionManagerService extends IPermissionManager.Stub { } if (permissionInfo.isAppOp()) { - return checkAppOpPermission(context, permission, attributionSource, message, - forDataDelivery, fromDatasource); + return checkAppOpPermission(context, permissionManagerServiceInt, permission, + attributionSource, message, forDataDelivery, fromDatasource); } if (permissionInfo.isRuntime()) { - return checkRuntimePermission(context, permission, attributionSource, message, - forDataDelivery, startDataDelivery, fromDatasource, attributedOp); + return checkRuntimePermission(context, permissionManagerServiceInt, permission, + attributionSource, message, forDataDelivery, startDataDelivery, + fromDatasource, attributedOp); } - if (!fromDatasource && !checkPermission(context, permission, attributionSource.getUid(), + if (!fromDatasource && !checkPermission(context, permissionManagerServiceInt, + permission, attributionSource.getUid(), attributionSource.getRenouncedPermissions())) { return PermissionChecker.PERMISSION_HARD_DENIED; } if (attributionSource.getNext() != null) { - return checkPermission(context, permission, attributionSource.getNext(), message, - forDataDelivery, startDataDelivery, /*fromDatasource*/ false, attributedOp); + return checkPermission(context, permissionManagerServiceInt, permission, + attributionSource.getNext(), message, forDataDelivery, startDataDelivery, + /*fromDatasource*/ false, attributedOp); } return PermissionChecker.PERMISSION_GRANTED; @@ -5624,6 +5648,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { @PermissionCheckerManager.PermissionResult private static int checkAppOpPermission(@NonNull Context context, + @NonNull PermissionManagerServiceInternal permissionManagerServiceInt, @NonNull String permission, @NonNull AttributionSource attributionSource, @Nullable String message, boolean forDataDelivery, boolean fromDatasource) { final int op = AppOpsManager.permissionToOpCode(permission); @@ -5667,13 +5692,13 @@ public class PermissionManagerService extends IPermissionManager.Stub { return PermissionChecker.PERMISSION_HARD_DENIED; } case AppOpsManager.MODE_DEFAULT: { - if (!skipCurrentChecks && !checkPermission(context, permission, - attributionSource.getUid(), attributionSource - .getRenouncedPermissions())) { + if (!skipCurrentChecks && !checkPermission(context, + permissionManagerServiceInt, permission, attributionSource.getUid(), + attributionSource.getRenouncedPermissions())) { return PermissionChecker.PERMISSION_HARD_DENIED; } - if (next != null && !checkPermission(context, permission, - next.getUid(), next.getRenouncedPermissions())) { + if (next != null && !checkPermission(context, permissionManagerServiceInt, + permission, next.getUid(), next.getRenouncedPermissions())) { return PermissionChecker.PERMISSION_HARD_DENIED; } } @@ -5688,6 +5713,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { } private static int checkRuntimePermission(@NonNull Context context, + @NonNull PermissionManagerServiceInternal permissionManagerServiceInt, @NonNull String permission, @NonNull AttributionSource attributionSource, @Nullable String message, boolean forDataDelivery, boolean startDataDelivery, boolean fromDatasource, int attributedOp) { @@ -5712,13 +5738,13 @@ public class PermissionManagerService extends IPermissionManager.Stub { } // If we already checked the permission for this one, skip the work - if (!skipCurrentChecks && !checkPermission(context, permission, - current.getUid(), current.getRenouncedPermissions())) { + if (!skipCurrentChecks && !checkPermission(context, permissionManagerServiceInt, + permission, current.getUid(), current.getRenouncedPermissions())) { return PermissionChecker.PERMISSION_HARD_DENIED; } - if (next != null && !checkPermission(context, permission, - next.getUid(), next.getRenouncedPermissions())) { + if (next != null && !checkPermission(context, permissionManagerServiceInt, + permission, next.getUid(), next.getRenouncedPermissions())) { return PermissionChecker.PERMISSION_HARD_DENIED; } @@ -5773,10 +5799,26 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } - private static boolean checkPermission(@NonNull Context context, @NonNull String permission, - int uid, @NonNull Set renouncedPermissions) { - final boolean permissionGranted = context.checkPermission(permission, /*pid*/ -1, + private static boolean checkPermission(@NonNull Context context, + @NonNull PermissionManagerServiceInternal permissionManagerServiceInt, + @NonNull String permission, int uid, @NonNull Set renouncedPermissions) { + boolean permissionGranted = context.checkPermission(permission, /*pid*/ -1, uid) == PackageManager.PERMISSION_GRANTED; + + // Override certain permissions checks for the HotwordDetectionService. This service is + // an isolated service, which ordinarily cannot hold permissions. + // There's probably a cleaner, more generalizable way to do this. For now, this is + // the only use case for this, so simply override here. + if (!permissionGranted + && Process.isIsolated(uid) // simple check which fails-fast for the common case + && (permission.equals(RECORD_AUDIO) + || permission.equals(CAPTURE_AUDIO_HOTWORD))) { + HotwordDetectionServiceProvider hotwordServiceProvider = + permissionManagerServiceInt.getHotwordDetectionServiceProvider(); + permissionGranted = hotwordServiceProvider != null + && uid == hotwordServiceProvider.getUid(); + } + if (permissionGranted && renouncedPermissions.contains(permission) && context.checkPermission(Manifest.permission.RENOUNCE_PERMISSIONS, /*pid*/ -1, uid) == PackageManager.PERMISSION_GRANTED) { diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java index 895cabc33bab1..f4fb8102d9229 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java @@ -428,4 +428,28 @@ public interface PermissionManagerServiceInternal extends PermissionManagerInter } } } + + /** + * Sets the provider of the currently active HotwordDetectionService. + * + * @see HotwordDetectionServiceProvider + */ + void setHotwordDetectionServiceProvider(@Nullable HotwordDetectionServiceProvider provider); + + /** + * Gets the provider of the currently active HotwordDetectionService. + * + * @see HotwordDetectionServiceProvider + */ + @Nullable + HotwordDetectionServiceProvider getHotwordDetectionServiceProvider(); + + /** + * Provides the uid of the currently active + * {@link android.service.voice.HotwordDetectionService}, which should be granted RECORD_AUDIO + * and CAPTURE_AUDIO_HOTWORD permissions. + */ + interface HotwordDetectionServiceProvider { + int getUid(); + } } diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java index a6c93deb2d4de..d97648813753d 100644 --- a/services/core/java/com/android/server/policy/AppOpsPolicy.java +++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java @@ -33,7 +33,10 @@ import android.content.pm.ResolveInfo; import android.location.LocationManagerInternal; import android.net.Uri; import android.os.IBinder; +import android.os.Process; import android.os.UserHandle; +import android.service.voice.VoiceInteractionManagerInternal; +import android.service.voice.VoiceInteractionManagerInternal.HotwordDetectionServiceIdentity; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; @@ -73,6 +76,9 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat @NonNull private final RoleManager mRoleManager; + @NonNull + private final VoiceInteractionManagerInternal mVoiceInteractionManagerInternal; + /** * The locking policy around the location tags is a bit special. Since we want to * avoid grabbing the lock on every op note we are taking the approach where the @@ -96,6 +102,8 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat public AppOpsPolicy(@NonNull Context context) { mContext = context; mRoleManager = mContext.getSystemService(RoleManager.class); + mVoiceInteractionManagerInternal = LocalServices.getService( + VoiceInteractionManagerInternal.class); final LocationManagerInternal locationManagerInternal = LocalServices.getService( LocationManagerInternal.class); @@ -145,7 +153,7 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat public int checkOperation(int code, int uid, String packageName, @Nullable String attributionTag, boolean raw, QuintFunction superImpl) { - return superImpl.apply(code, uid, packageName, attributionTag, raw); + return superImpl.apply(code, resolveUid(code, uid), packageName, attributionTag, raw); } @Override @@ -159,8 +167,8 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat @Nullable String attributionTag, boolean shouldCollectAsyncNotedOp, @Nullable String message, boolean shouldCollectMessage, @NonNull HeptFunction superImpl) { - return superImpl.apply(resolveDatasourceOp(code, uid, packageName, attributionTag), uid, - packageName, attributionTag, shouldCollectAsyncNotedOp, + return superImpl.apply(resolveDatasourceOp(code, uid, packageName, attributionTag), + resolveUid(code, uid), packageName, attributionTag, shouldCollectAsyncNotedOp, message, shouldCollectMessage); } @@ -185,8 +193,9 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat String, Boolean, Boolean, String, Boolean, Integer, Integer, SyncNotedAppOp> superImpl) { return superImpl.apply(token, resolveDatasourceOp(code, uid, packageName, attributionTag), - uid, packageName, attributionTag, startIfModeDefault, shouldCollectAsyncNotedOp, - message, shouldCollectMessage, attributionFlags, attributionChainId); + resolveUid(code, uid), packageName, attributionTag, startIfModeDefault, + shouldCollectAsyncNotedOp, message, shouldCollectMessage, attributionFlags, + attributionChainId); } @Override @@ -387,4 +396,23 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat } return code; } + + private int resolveUid(int code, int uid) { + // The HotwordDetectionService is an isolated service, which ordinarily cannot hold + // permissions. So we allow it to assume the owning package identity for certain + // operations. + // Note: The package name coming from the audio server is already the one for the owning + // package, so we don't need to modify it. + if (Process.isIsolated(uid) // simple check which fails-fast for the common case + && (code == AppOpsManager.OP_RECORD_AUDIO + || code == AppOpsManager.OP_RECORD_AUDIO_HOTWORD)) { + final HotwordDetectionServiceIdentity hotwordDetectionServiceIdentity = + mVoiceInteractionManagerInternal.getHotwordDetectionServiceIdentity(); + if (hotwordDetectionServiceIdentity != null + && uid == hotwordDetectionServiceIdentity.getIsolatedUid()) { + uid = hotwordDetectionServiceIdentity.getOwnerUid(); + } + } + return uid; + } } diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java index 0bb09a9de07f8..326d604e5dff1 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java @@ -34,6 +34,7 @@ import android.media.AudioFormat; import android.media.AudioManager; import android.media.AudioRecord; import android.media.MediaRecorder; +import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.os.IRemoteCallback; @@ -48,6 +49,7 @@ import android.service.voice.HotwordRejectedResult; import android.service.voice.IDspHotwordDetectionCallback; import android.service.voice.IHotwordDetectionService; import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback; +import android.service.voice.VoiceInteractionManagerInternal.HotwordDetectionServiceIdentity; import android.util.Pair; import android.util.Slog; import android.view.contentcapture.IContentCaptureManager; @@ -56,6 +58,8 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.app.IHotwordRecognitionStatusCallback; import com.android.internal.infra.AndroidFuture; import com.android.internal.infra.ServiceConnector; +import com.android.server.LocalServices; +import com.android.server.pm.permission.PermissionManagerServiceInternal; import java.io.Closeable; import java.io.IOException; @@ -94,20 +98,24 @@ final class HotwordDetectionConnection { private final AtomicBoolean mUpdateStateFinish = new AtomicBoolean(false); final Object mLock; + final int mVoiceInteractionServiceUid; final ComponentName mDetectionComponentName; final int mUser; final Context mContext; final @NonNull ServiceConnector mRemoteHotwordDetectionService; boolean mBound; + volatile HotwordDetectionServiceIdentity mIdentity; @GuardedBy("mLock") private ParcelFileDescriptor mCurrentAudioSink; - HotwordDetectionConnection(Object lock, Context context, ComponentName serviceName, - int userId, boolean bindInstantServiceAllowed, @Nullable PersistableBundle options, - @Nullable SharedMemory sharedMemory, IHotwordRecognitionStatusCallback callback) { + HotwordDetectionConnection(Object lock, Context context, int voiceInteractionServiceUid, + ComponentName serviceName, int userId, boolean bindInstantServiceAllowed, + @Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory, + IHotwordRecognitionStatusCallback callback) { mLock = lock; mContext = context; + mVoiceInteractionServiceUid = voiceInteractionServiceUid; mDetectionComponentName = serviceName; mUser = userId; final Intent intent = new Intent(HotwordDetectionService.SERVICE_INTERFACE); @@ -153,7 +161,15 @@ final class HotwordDetectionConnection { public void sendResult(Bundle bundle) throws RemoteException { if (DEBUG) { Slog.d(TAG, "updateState finish"); + Slog.d(TAG, "updating hotword UID " + Binder.getCallingUid()); } + // TODO: Do this earlier than this callback and have the provider point to the + // current state stored in VoiceInteractionManagerServiceImpl. + final int uid = Binder.getCallingUid(); + LocalServices.getService(PermissionManagerServiceInternal.class) + .setHotwordDetectionServiceProvider(() -> uid); + mIdentity = + new HotwordDetectionServiceIdentity(uid, mVoiceInteractionServiceUid); future.complete(null); try { if (mUpdateStateFinish.getAndSet(true)) { @@ -224,6 +240,9 @@ final class HotwordDetectionConnection { if (mBound) { mRemoteHotwordDetectionService.unbind(); mBound = false; + LocalServices.getService(PermissionManagerServiceInternal.class) + .setHotwordDetectionServiceProvider(null); + mIdentity = null; } } diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index f3d80b13f2900..a14912e5593db 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -251,6 +251,26 @@ public class VoiceInteractionManagerService extends SystemService { return TextUtils.equals(packageName, session.mSessionComponentName.getPackageName()); } + + @Override + public HotwordDetectionServiceIdentity getHotwordDetectionServiceIdentity() { + // IMPORTANT: This is called when performing permission checks; do not lock! + + // TODO: Have AppOpsPolicy register a listener instead of calling in here everytime. + // Then also remove the `volatile`s that were added with this method. + + VoiceInteractionManagerServiceImpl impl = + VoiceInteractionManagerService.this.mServiceStub.mImpl; + if (impl == null) { + return null; + } + HotwordDetectionConnection hotwordDetectionConnection = + impl.mHotwordDetectionConnection; + if (hotwordDetectionConnection == null) { + return null; + } + return hotwordDetectionConnection.mIdentity; + } } // implementation entry point and binder service @@ -258,7 +278,7 @@ public class VoiceInteractionManagerService extends SystemService { class VoiceInteractionManagerServiceStub extends IVoiceInteractionManagerService.Stub { - VoiceInteractionManagerServiceImpl mImpl; + volatile VoiceInteractionManagerServiceImpl mImpl; private boolean mSafeMode; private int mCurUser; diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java index 2206b0a61b9e3..ca30bc51e0464 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java @@ -98,7 +98,7 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne final ComponentName mHotwordDetectionComponentName; boolean mBound = false; IVoiceInteractionService mService; - HotwordDetectionConnection mHotwordDetectionConnection; + volatile HotwordDetectionConnection mHotwordDetectionConnection; VoiceInteractionSessionConnection mActiveSession; int mDisabledShowContext; @@ -447,8 +447,8 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne if (mHotwordDetectionConnection == null) { mHotwordDetectionConnection = new HotwordDetectionConnection(mServiceStub, mContext, - mHotwordDetectionComponentName, mUser, /* bindInstantServiceAllowed= */ false, - options, sharedMemory, callback); + mInfo.getServiceInfo().applicationInfo.uid, mHotwordDetectionComponentName, + mUser, /* bindInstantServiceAllowed= */ false, options, sharedMemory, callback); } else { mHotwordDetectionConnection.updateStateLocked(options, sharedMemory); }