Merge "Grant HotwordDetectionService RECORD_AUDIO and CAPTURE_AUDIO_HOTWORD." into sc-dev

This commit is contained in:
Ahaan Ugale
2021-06-10 17:53:56 +00:00
committed by Android (Google) Code Review
7 changed files with 207 additions and 37 deletions

View File

@@ -16,9 +16,12 @@
package android.service.voice; package android.service.voice;
import android.annotation.Nullable;
import android.os.Bundle; import android.os.Bundle;
import android.os.IBinder; import android.os.IBinder;
import com.android.internal.annotations.Immutable;
/** /**
* @hide * @hide
@@ -46,4 +49,38 @@ public abstract class VoiceInteractionManagerInternal {
* Returns whether the given package is currently in an active session * Returns whether the given package is currently in an active session
*/ */
public abstract boolean hasActiveSession(String packageName); 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;
}
}
} }

View File

@@ -17,7 +17,9 @@
package com.android.server.pm.permission; package com.android.server.pm.permission;
import static android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY; 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.READ_EXTERNAL_STORAGE;
import static android.Manifest.permission.RECORD_AUDIO;
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
import static android.app.AppOpsManager.MODE_ALLOWED; import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_ERRORED; 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.UserManagerService;
import com.android.server.pm.parsing.PackageInfoUtils; import com.android.server.pm.parsing.PackageInfoUtils;
import com.android.server.pm.parsing.pkg.AndroidPackage; 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.pm.permission.PermissionManagerServiceInternal.OnRuntimePermissionStateChangedListener;
import com.android.server.policy.PermissionPolicyInternal; import com.android.server.policy.PermissionPolicyInternal;
import com.android.server.policy.SoftRestrictedPermissionPolicy; import com.android.server.policy.SoftRestrictedPermissionPolicy;
@@ -308,6 +311,9 @@ public class PermissionManagerService extends IPermissionManager.Stub {
@NonNull @NonNull
private final OnPermissionChangeListeners mOnPermissionChangeListeners; private final OnPermissionChangeListeners mOnPermissionChangeListeners;
@Nullable
private HotwordDetectionServiceProvider mHotwordDetectionServiceProvider;
// TODO: Take a look at the methods defined in the callback. // TODO: Take a look at the methods defined in the callback.
// The callback was initially created to support the split between permission // 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 // 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) { public int[] getGidsForUid(int uid) {
return PermissionManagerService.this.getGidsForUid(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 Context mContext;
private final @NonNull AppOpsManager mAppOpsManager; private final @NonNull AppOpsManager mAppOpsManager;
private final @NonNull PermissionManagerServiceInternal mPermissionManagerServiceInternal;
PermissionCheckerService(@NonNull Context context) { PermissionCheckerService(@NonNull Context context) {
mContext = context; mContext = context;
mAppOpsManager = mContext.getSystemService(AppOpsManager.class); mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
mPermissionManagerServiceInternal =
LocalServices.getService(PermissionManagerServiceInternal.class);
} }
@Override @Override
@@ -5492,8 +5511,9 @@ public class PermissionManagerService extends IPermissionManager.Stub {
Objects.requireNonNull(attributionSourceState); Objects.requireNonNull(attributionSourceState);
final AttributionSource attributionSource = new AttributionSource( final AttributionSource attributionSource = new AttributionSource(
attributionSourceState); attributionSourceState);
final int result = checkPermission(mContext, permission, attributionSource, message, final int result = checkPermission(mContext, mPermissionManagerServiceInternal,
forDataDelivery, startDataDelivery, fromDatasource, attributedOp); permission, attributionSource, message, forDataDelivery, startDataDelivery,
fromDatasource, attributedOp);
// Finish any started op if some step in the attribution chain failed. // Finish any started op if some step in the attribution chain failed.
if (startDataDelivery && result != PermissionChecker.PERMISSION_GRANTED if (startDataDelivery && result != PermissionChecker.PERMISSION_GRANTED
&& result != PermissionChecker.PERMISSION_SOFT_DENIED) { && result != PermissionChecker.PERMISSION_SOFT_DENIED) {
@@ -5582,10 +5602,11 @@ public class PermissionManagerService extends IPermissionManager.Stub {
} }
@PermissionCheckerManager.PermissionResult @PermissionCheckerManager.PermissionResult
private static int checkPermission(@NonNull Context context, @NonNull String permission, private static int checkPermission(@NonNull Context context,
@NonNull AttributionSource attributionSource, @Nullable String message, @NonNull PermissionManagerServiceInternal permissionManagerServiceInt,
boolean forDataDelivery, boolean startDataDelivery, boolean fromDatasource, @NonNull String permission, @NonNull AttributionSource attributionSource,
int attributedOp) { @Nullable String message, boolean forDataDelivery, boolean startDataDelivery,
boolean fromDatasource, int attributedOp) {
PermissionInfo permissionInfo = sPlatformPermissions.get(permission); PermissionInfo permissionInfo = sPlatformPermissions.get(permission);
if (permissionInfo == null) { if (permissionInfo == null) {
@@ -5602,22 +5623,25 @@ public class PermissionManagerService extends IPermissionManager.Stub {
} }
if (permissionInfo.isAppOp()) { if (permissionInfo.isAppOp()) {
return checkAppOpPermission(context, permission, attributionSource, message, return checkAppOpPermission(context, permissionManagerServiceInt, permission,
forDataDelivery, fromDatasource); attributionSource, message, forDataDelivery, fromDatasource);
} }
if (permissionInfo.isRuntime()) { if (permissionInfo.isRuntime()) {
return checkRuntimePermission(context, permission, attributionSource, message, return checkRuntimePermission(context, permissionManagerServiceInt, permission,
forDataDelivery, startDataDelivery, fromDatasource, attributedOp); attributionSource, message, forDataDelivery, startDataDelivery,
fromDatasource, attributedOp);
} }
if (!fromDatasource && !checkPermission(context, permission, attributionSource.getUid(), if (!fromDatasource && !checkPermission(context, permissionManagerServiceInt,
permission, attributionSource.getUid(),
attributionSource.getRenouncedPermissions())) { attributionSource.getRenouncedPermissions())) {
return PermissionChecker.PERMISSION_HARD_DENIED; return PermissionChecker.PERMISSION_HARD_DENIED;
} }
if (attributionSource.getNext() != null) { if (attributionSource.getNext() != null) {
return checkPermission(context, permission, attributionSource.getNext(), message, return checkPermission(context, permissionManagerServiceInt, permission,
forDataDelivery, startDataDelivery, /*fromDatasource*/ false, attributedOp); attributionSource.getNext(), message, forDataDelivery, startDataDelivery,
/*fromDatasource*/ false, attributedOp);
} }
return PermissionChecker.PERMISSION_GRANTED; return PermissionChecker.PERMISSION_GRANTED;
@@ -5625,6 +5649,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
@PermissionCheckerManager.PermissionResult @PermissionCheckerManager.PermissionResult
private static int checkAppOpPermission(@NonNull Context context, private static int checkAppOpPermission(@NonNull Context context,
@NonNull PermissionManagerServiceInternal permissionManagerServiceInt,
@NonNull String permission, @NonNull AttributionSource attributionSource, @NonNull String permission, @NonNull AttributionSource attributionSource,
@Nullable String message, boolean forDataDelivery, boolean fromDatasource) { @Nullable String message, boolean forDataDelivery, boolean fromDatasource) {
final int op = AppOpsManager.permissionToOpCode(permission); final int op = AppOpsManager.permissionToOpCode(permission);
@@ -5668,13 +5693,13 @@ public class PermissionManagerService extends IPermissionManager.Stub {
return PermissionChecker.PERMISSION_HARD_DENIED; return PermissionChecker.PERMISSION_HARD_DENIED;
} }
case AppOpsManager.MODE_DEFAULT: { case AppOpsManager.MODE_DEFAULT: {
if (!skipCurrentChecks && !checkPermission(context, permission, if (!skipCurrentChecks && !checkPermission(context,
attributionSource.getUid(), attributionSource permissionManagerServiceInt, permission, attributionSource.getUid(),
.getRenouncedPermissions())) { attributionSource.getRenouncedPermissions())) {
return PermissionChecker.PERMISSION_HARD_DENIED; return PermissionChecker.PERMISSION_HARD_DENIED;
} }
if (next != null && !checkPermission(context, permission, if (next != null && !checkPermission(context, permissionManagerServiceInt,
next.getUid(), next.getRenouncedPermissions())) { permission, next.getUid(), next.getRenouncedPermissions())) {
return PermissionChecker.PERMISSION_HARD_DENIED; return PermissionChecker.PERMISSION_HARD_DENIED;
} }
} }
@@ -5689,6 +5714,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
} }
private static int checkRuntimePermission(@NonNull Context context, private static int checkRuntimePermission(@NonNull Context context,
@NonNull PermissionManagerServiceInternal permissionManagerServiceInt,
@NonNull String permission, @NonNull AttributionSource attributionSource, @NonNull String permission, @NonNull AttributionSource attributionSource,
@Nullable String message, boolean forDataDelivery, boolean startDataDelivery, @Nullable String message, boolean forDataDelivery, boolean startDataDelivery,
boolean fromDatasource, int attributedOp) { boolean fromDatasource, int attributedOp) {
@@ -5713,13 +5739,13 @@ public class PermissionManagerService extends IPermissionManager.Stub {
} }
// If we already checked the permission for this one, skip the work // If we already checked the permission for this one, skip the work
if (!skipCurrentChecks && !checkPermission(context, permission, if (!skipCurrentChecks && !checkPermission(context, permissionManagerServiceInt,
current.getUid(), current.getRenouncedPermissions())) { permission, current.getUid(), current.getRenouncedPermissions())) {
return PermissionChecker.PERMISSION_HARD_DENIED; return PermissionChecker.PERMISSION_HARD_DENIED;
} }
if (next != null && !checkPermission(context, permission, if (next != null && !checkPermission(context, permissionManagerServiceInt,
next.getUid(), next.getRenouncedPermissions())) { permission, next.getUid(), next.getRenouncedPermissions())) {
return PermissionChecker.PERMISSION_HARD_DENIED; return PermissionChecker.PERMISSION_HARD_DENIED;
} }
@@ -5774,10 +5800,26 @@ public class PermissionManagerService extends IPermissionManager.Stub {
} }
} }
private static boolean checkPermission(@NonNull Context context, @NonNull String permission, private static boolean checkPermission(@NonNull Context context,
int uid, @NonNull Set<String> renouncedPermissions) { @NonNull PermissionManagerServiceInternal permissionManagerServiceInt,
final boolean permissionGranted = context.checkPermission(permission, /*pid*/ -1, @NonNull String permission, int uid, @NonNull Set<String> renouncedPermissions) {
boolean permissionGranted = context.checkPermission(permission, /*pid*/ -1,
uid) == PackageManager.PERMISSION_GRANTED; 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) if (permissionGranted && renouncedPermissions.contains(permission)
&& context.checkPermission(Manifest.permission.RENOUNCE_PERMISSIONS, && context.checkPermission(Manifest.permission.RENOUNCE_PERMISSIONS,
/*pid*/ -1, uid) == PackageManager.PERMISSION_GRANTED) { /*pid*/ -1, uid) == PackageManager.PERMISSION_GRANTED) {

View File

@@ -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();
}
} }

View File

@@ -33,7 +33,10 @@ import android.content.pm.ResolveInfo;
import android.location.LocationManagerInternal; import android.location.LocationManagerInternal;
import android.net.Uri; import android.net.Uri;
import android.os.IBinder; import android.os.IBinder;
import android.os.Process;
import android.os.UserHandle; import android.os.UserHandle;
import android.service.voice.VoiceInteractionManagerInternal;
import android.service.voice.VoiceInteractionManagerInternal.HotwordDetectionServiceIdentity;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.ArrayMap; import android.util.ArrayMap;
import android.util.ArraySet; import android.util.ArraySet;
@@ -78,6 +81,9 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat
@NonNull @NonNull
private final RoleManager mRoleManager; private final RoleManager mRoleManager;
@NonNull
private final VoiceInteractionManagerInternal mVoiceInteractionManagerInternal;
/** /**
* The locking policy around the location tags is a bit special. Since we want to * 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 * avoid grabbing the lock on every op note we are taking the approach where the
@@ -101,6 +107,8 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat
public AppOpsPolicy(@NonNull Context context) { public AppOpsPolicy(@NonNull Context context) {
mContext = context; mContext = context;
mRoleManager = mContext.getSystemService(RoleManager.class); mRoleManager = mContext.getSystemService(RoleManager.class);
mVoiceInteractionManagerInternal = LocalServices.getService(
VoiceInteractionManagerInternal.class);
final LocationManagerInternal locationManagerInternal = LocalServices.getService( final LocationManagerInternal locationManagerInternal = LocalServices.getService(
LocationManagerInternal.class); LocationManagerInternal.class);
@@ -150,7 +158,7 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat
public int checkOperation(int code, int uid, String packageName, public int checkOperation(int code, int uid, String packageName,
@Nullable String attributionTag, boolean raw, @Nullable String attributionTag, boolean raw,
QuintFunction<Integer, Integer, String, String, Boolean, Integer> superImpl) { QuintFunction<Integer, Integer, String, String, Boolean, Integer> superImpl) {
return superImpl.apply(code, uid, packageName, attributionTag, raw); return superImpl.apply(code, resolveUid(code, uid), packageName, attributionTag, raw);
} }
@Override @Override
@@ -164,8 +172,8 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat
@Nullable String attributionTag, boolean shouldCollectAsyncNotedOp, @Nullable @Nullable String attributionTag, boolean shouldCollectAsyncNotedOp, @Nullable
String message, boolean shouldCollectMessage, @NonNull HeptFunction<Integer, Integer, String message, boolean shouldCollectMessage, @NonNull HeptFunction<Integer, Integer,
String, String, Boolean, String, Boolean, SyncNotedAppOp> superImpl) { String, String, Boolean, String, Boolean, SyncNotedAppOp> superImpl) {
return superImpl.apply(resolveDatasourceOp(code, uid, packageName, attributionTag), uid, return superImpl.apply(resolveDatasourceOp(code, uid, packageName, attributionTag),
packageName, attributionTag, shouldCollectAsyncNotedOp, resolveUid(code, uid), packageName, attributionTag, shouldCollectAsyncNotedOp,
message, shouldCollectMessage); message, shouldCollectMessage);
} }
@@ -190,8 +198,9 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat
String, Boolean, Boolean, String, Boolean, Integer, Integer, String, Boolean, Boolean, String, Boolean, Integer, Integer,
SyncNotedAppOp> superImpl) { SyncNotedAppOp> superImpl) {
return superImpl.apply(token, resolveDatasourceOp(code, uid, packageName, attributionTag), return superImpl.apply(token, resolveDatasourceOp(code, uid, packageName, attributionTag),
uid, packageName, attributionTag, startIfModeDefault, shouldCollectAsyncNotedOp, resolveUid(code, uid), packageName, attributionTag, startIfModeDefault,
message, shouldCollectMessage, attributionFlags, attributionChainId); shouldCollectAsyncNotedOp, message, shouldCollectMessage, attributionFlags,
attributionChainId);
} }
@Override @Override
@@ -404,4 +413,23 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat
} }
return code; 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;
}
} }

View File

@@ -34,6 +34,7 @@ import android.media.AudioFormat;
import android.media.AudioManager; import android.media.AudioManager;
import android.media.AudioRecord; import android.media.AudioRecord;
import android.media.MediaRecorder; import android.media.MediaRecorder;
import android.os.Binder;
import android.os.Bundle; import android.os.Bundle;
import android.os.IBinder; import android.os.IBinder;
import android.os.IRemoteCallback; import android.os.IRemoteCallback;
@@ -48,6 +49,7 @@ import android.service.voice.HotwordRejectedResult;
import android.service.voice.IDspHotwordDetectionCallback; import android.service.voice.IDspHotwordDetectionCallback;
import android.service.voice.IHotwordDetectionService; import android.service.voice.IHotwordDetectionService;
import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback; import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback;
import android.service.voice.VoiceInteractionManagerInternal.HotwordDetectionServiceIdentity;
import android.util.Pair; import android.util.Pair;
import android.util.Slog; import android.util.Slog;
import android.view.contentcapture.IContentCaptureManager; 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.app.IHotwordRecognitionStatusCallback;
import com.android.internal.infra.AndroidFuture; import com.android.internal.infra.AndroidFuture;
import com.android.internal.infra.ServiceConnector; 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.Closeable;
import java.io.IOException; import java.io.IOException;
@@ -94,20 +98,24 @@ final class HotwordDetectionConnection {
private final AtomicBoolean mUpdateStateFinish = new AtomicBoolean(false); private final AtomicBoolean mUpdateStateFinish = new AtomicBoolean(false);
final Object mLock; final Object mLock;
final int mVoiceInteractionServiceUid;
final ComponentName mDetectionComponentName; final ComponentName mDetectionComponentName;
final int mUser; final int mUser;
final Context mContext; final Context mContext;
final @NonNull ServiceConnector<IHotwordDetectionService> mRemoteHotwordDetectionService; final @NonNull ServiceConnector<IHotwordDetectionService> mRemoteHotwordDetectionService;
boolean mBound; boolean mBound;
volatile HotwordDetectionServiceIdentity mIdentity;
@GuardedBy("mLock") @GuardedBy("mLock")
private ParcelFileDescriptor mCurrentAudioSink; private ParcelFileDescriptor mCurrentAudioSink;
HotwordDetectionConnection(Object lock, Context context, ComponentName serviceName, HotwordDetectionConnection(Object lock, Context context, int voiceInteractionServiceUid,
int userId, boolean bindInstantServiceAllowed, @Nullable PersistableBundle options, ComponentName serviceName, int userId, boolean bindInstantServiceAllowed,
@Nullable SharedMemory sharedMemory, IHotwordRecognitionStatusCallback callback) { @Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory,
IHotwordRecognitionStatusCallback callback) {
mLock = lock; mLock = lock;
mContext = context; mContext = context;
mVoiceInteractionServiceUid = voiceInteractionServiceUid;
mDetectionComponentName = serviceName; mDetectionComponentName = serviceName;
mUser = userId; mUser = userId;
final Intent intent = new Intent(HotwordDetectionService.SERVICE_INTERFACE); final Intent intent = new Intent(HotwordDetectionService.SERVICE_INTERFACE);
@@ -164,7 +172,15 @@ final class HotwordDetectionConnection {
public void sendResult(Bundle bundle) throws RemoteException { public void sendResult(Bundle bundle) throws RemoteException {
if (DEBUG) { if (DEBUG) {
Slog.d(TAG, "updateState finish"); 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); future.complete(null);
try { try {
if (mUpdateStateFinish.getAndSet(true)) { if (mUpdateStateFinish.getAndSet(true)) {
@@ -235,6 +251,9 @@ final class HotwordDetectionConnection {
if (mBound) { if (mBound) {
mRemoteHotwordDetectionService.unbind(); mRemoteHotwordDetectionService.unbind();
mBound = false; mBound = false;
LocalServices.getService(PermissionManagerServiceInternal.class)
.setHotwordDetectionServiceProvider(null);
mIdentity = null;
} }
} }

View File

@@ -251,6 +251,26 @@ public class VoiceInteractionManagerService extends SystemService {
return TextUtils.equals(packageName, session.mSessionComponentName.getPackageName()); 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 // implementation entry point and binder service
@@ -258,7 +278,7 @@ public class VoiceInteractionManagerService extends SystemService {
class VoiceInteractionManagerServiceStub extends IVoiceInteractionManagerService.Stub { class VoiceInteractionManagerServiceStub extends IVoiceInteractionManagerService.Stub {
VoiceInteractionManagerServiceImpl mImpl; volatile VoiceInteractionManagerServiceImpl mImpl;
private boolean mSafeMode; private boolean mSafeMode;
private int mCurUser; private int mCurUser;

View File

@@ -98,7 +98,7 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
final ComponentName mHotwordDetectionComponentName; final ComponentName mHotwordDetectionComponentName;
boolean mBound = false; boolean mBound = false;
IVoiceInteractionService mService; IVoiceInteractionService mService;
HotwordDetectionConnection mHotwordDetectionConnection; volatile HotwordDetectionConnection mHotwordDetectionConnection;
VoiceInteractionSessionConnection mActiveSession; VoiceInteractionSessionConnection mActiveSession;
int mDisabledShowContext; int mDisabledShowContext;
@@ -447,8 +447,8 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
if (mHotwordDetectionConnection == null) { if (mHotwordDetectionConnection == null) {
mHotwordDetectionConnection = new HotwordDetectionConnection(mServiceStub, mContext, mHotwordDetectionConnection = new HotwordDetectionConnection(mServiceStub, mContext,
mHotwordDetectionComponentName, mUser, /* bindInstantServiceAllowed= */ false, mInfo.getServiceInfo().applicationInfo.uid, mHotwordDetectionComponentName,
options, sharedMemory, callback); mUser, /* bindInstantServiceAllowed= */ false, options, sharedMemory, callback);
} else { } else {
mHotwordDetectionConnection.updateStateLocked(options, sharedMemory); mHotwordDetectionConnection.updateStateLocked(options, sharedMemory);
} }