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
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<String> 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<String> 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) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<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
|
||||
@@ -159,8 +167,8 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat
|
||||
@Nullable String attributionTag, boolean shouldCollectAsyncNotedOp, @Nullable
|
||||
String message, boolean shouldCollectMessage, @NonNull HeptFunction<Integer, Integer,
|
||||
String, String, Boolean, String, Boolean, SyncNotedAppOp> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<IHotwordDetectionService> 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user