Loading services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java +1 −1 Original line number Diff line number Diff line Loading @@ -79,7 +79,7 @@ import java.util.function.Function; final class HotwordDetectionConnection { private static final String TAG = "HotwordDetectionConnection"; // TODO (b/177502877): Set the Debug flag to false before shipping. private static final boolean DEBUG = true; static final boolean DEBUG = true; // TODO: These constants need to be refined. private static final long VALIDATION_TIMEOUT_MILLIS = 3000; Loading services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerSessionBinderProxy.java 0 → 100644 +72 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.voiceinteraction; import android.hardware.soundtrigger.SoundTrigger; import android.os.RemoteException; import com.android.internal.app.IHotwordRecognitionStatusCallback; import com.android.internal.app.IVoiceInteractionSoundTriggerSession; /** * A remote object that simply proxies calls to a real {@link IVoiceInteractionSoundTriggerSession} * implementation. This design pattern allows us to add decorators to the core implementation * (simply wrapping a binder object does not work). */ final class SoundTriggerSessionBinderProxy extends IVoiceInteractionSoundTriggerSession.Stub { private final IVoiceInteractionSoundTriggerSession mDelegate; SoundTriggerSessionBinderProxy(IVoiceInteractionSoundTriggerSession delegate) { mDelegate = delegate; } @Override public SoundTrigger.ModuleProperties getDspModuleProperties() throws RemoteException { return mDelegate.getDspModuleProperties(); } @Override public int startRecognition(int i, String s, IHotwordRecognitionStatusCallback iHotwordRecognitionStatusCallback, SoundTrigger.RecognitionConfig recognitionConfig, boolean b) throws RemoteException { return mDelegate.startRecognition(i, s, iHotwordRecognitionStatusCallback, recognitionConfig, b); } @Override public int stopRecognition(int i, IHotwordRecognitionStatusCallback iHotwordRecognitionStatusCallback) throws RemoteException { return mDelegate.stopRecognition(i, iHotwordRecognitionStatusCallback); } @Override public int setParameter(int i, int i1, int i2) throws RemoteException { return mDelegate.setParameter(i, i1, i2); } @Override public int getParameter(int i, int i1) throws RemoteException { return mDelegate.getParameter(i, i1); } @Override public SoundTrigger.ModelParamRange queryParameter(int i, int i1) throws RemoteException { return mDelegate.queryParameter(i, i1); } } services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerSessionPermissionsDecorator.java 0 → 100644 +158 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.voiceinteraction; import static android.Manifest.permission.CAPTURE_AUDIO_HOTWORD; import static android.Manifest.permission.RECORD_AUDIO; import static com.android.server.voiceinteraction.HotwordDetectionConnection.DEBUG; import android.annotation.NonNull; import android.content.Context; import android.content.PermissionChecker; import android.hardware.soundtrigger.SoundTrigger; import android.media.permission.Identity; import android.media.permission.PermissionUtil; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.text.TextUtils; import android.util.Slog; import com.android.internal.app.IHotwordRecognitionStatusCallback; import com.android.internal.app.IVoiceInteractionSoundTriggerSession; /** * Decorates {@link IVoiceInteractionSoundTriggerSession} with permission checks for {@link * android.Manifest.permission#RECORD_AUDIO} and * {@link android.Manifest.permission#CAPTURE_AUDIO_HOTWORD}. * <p> * Does not implement {@link #asBinder()} as it's intended to be wrapped by an * {@link IVoiceInteractionSoundTriggerSession.Stub} object. */ final class SoundTriggerSessionPermissionsDecorator implements IVoiceInteractionSoundTriggerSession { static final String TAG = "SoundTriggerSessionPermissionsDecorator"; private final IVoiceInteractionSoundTriggerSession mDelegate; private final Context mContext; private final Identity mOriginatorIdentity; SoundTriggerSessionPermissionsDecorator(IVoiceInteractionSoundTriggerSession delegate, Context context, Identity originatorIdentity) { mDelegate = delegate; mContext = context; mOriginatorIdentity = originatorIdentity; } @Override public SoundTrigger.ModuleProperties getDspModuleProperties() throws RemoteException { // No permission needed. return mDelegate.getDspModuleProperties(); } @Override public int startRecognition(int i, String s, IHotwordRecognitionStatusCallback iHotwordRecognitionStatusCallback, SoundTrigger.RecognitionConfig recognitionConfig, boolean b) throws RemoteException { if (DEBUG) { Slog.d(TAG, "startRecognition"); } enforcePermissions(); return mDelegate.startRecognition(i, s, iHotwordRecognitionStatusCallback, recognitionConfig, b); } @Override public int stopRecognition(int i, IHotwordRecognitionStatusCallback iHotwordRecognitionStatusCallback) throws RemoteException { enforcePermissions(); return mDelegate.stopRecognition(i, iHotwordRecognitionStatusCallback); } @Override public int setParameter(int i, int i1, int i2) throws RemoteException { enforcePermissions(); return mDelegate.setParameter(i, i1, i2); } @Override public int getParameter(int i, int i1) throws RemoteException { enforcePermissions(); return mDelegate.getParameter(i, i1); } @Override public SoundTrigger.ModelParamRange queryParameter(int i, int i1) throws RemoteException { enforcePermissions(); return mDelegate.queryParameter(i, i1); } @Override public IBinder asBinder() { throw new UnsupportedOperationException( "This object isn't intended to be used as a Binder."); } // TODO: Share this code with SoundTriggerMiddlewarePermission. private void enforcePermissions() { enforcePermissionForPreflight(mContext, mOriginatorIdentity, RECORD_AUDIO); enforcePermissionForPreflight(mContext, mOriginatorIdentity, CAPTURE_AUDIO_HOTWORD); } /** * Throws a {@link SecurityException} if originator permanently doesn't have the given * permission, or a {@link ServiceSpecificException} with a {@link * #TEMPORARY_PERMISSION_DENIED} if caller originator doesn't have the given permission. * * @param context A {@link Context}, used for permission checks. * @param identity The identity to check. * @param permission The identifier of the permission we want to check. */ private static void enforcePermissionForPreflight(@NonNull Context context, @NonNull Identity identity, @NonNull String permission) { final int status = PermissionUtil.checkPermissionForPreflight(context, identity, permission); switch (status) { case PermissionChecker.PERMISSION_GRANTED: return; case PermissionChecker.PERMISSION_HARD_DENIED: throw new SecurityException( TextUtils.formatSimple("Failed to obtain permission %s for identity %s", permission, toString(identity))); case PermissionChecker.PERMISSION_SOFT_DENIED: throw new ServiceSpecificException(TEMPORARY_PERMISSION_DENIED, TextUtils.formatSimple("Failed to obtain permission %s for identity %s", permission, toString(identity))); default: throw new RuntimeException("Unexpected permission check result."); } } private static String toString(Identity identity) { return "{uid=" + identity.uid + " pid=" + identity.pid + " packageName=" + identity.packageName + " attributionTag=" + identity.attributionTag + "}"; } // Temporary hack for using the same status code as SoundTrigger, so we don't change behavior. // TODO: Reuse SoundTrigger code so we don't need to do this. private static final int TEMPORARY_PERMISSION_DENIED = 3; } services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +55 −28 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.ActivityThread; import android.app.AppGlobals; import android.app.role.OnRoleHoldersChangedListener; import android.app.role.RoleManager; Loading Loading @@ -50,6 +51,7 @@ import android.hardware.soundtrigger.SoundTrigger.ModuleProperties; import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig; import android.media.AudioFormat; import android.media.permission.Identity; import android.media.permission.IdentityContext; import android.media.permission.PermissionUtil; import android.media.permission.SafeCloseable; import android.os.Binder; Loading @@ -59,6 +61,7 @@ import android.os.IBinder; import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; import android.os.Process; import android.os.RemoteCallback; import android.os.RemoteCallbackList; import android.os.RemoteException; Loading Loading @@ -104,11 +107,8 @@ import com.android.server.wm.ActivityTaskManagerInternal; import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Locale; import java.util.Objects; import java.util.concurrent.Executor; Loading Loading @@ -288,7 +288,6 @@ public class VoiceInteractionManagerService extends SystemService { private boolean mTemporarilyDisabled; private final boolean mEnableService; private final List<WeakReference<SoundTriggerSession>> mSessions = new LinkedList<>(); VoiceInteractionManagerServiceStub() { mEnableService = shouldEnableService(mContext); Loading @@ -299,15 +298,49 @@ public class VoiceInteractionManagerService extends SystemService { public @NonNull IVoiceInteractionSoundTriggerSession createSoundTriggerSessionAsOriginator( @NonNull Identity originatorIdentity, IBinder client) { Objects.requireNonNull(originatorIdentity); boolean forHotwordDetectionService; synchronized (VoiceInteractionManagerServiceStub.this) { enforceIsCurrentVoiceInteractionService(); forHotwordDetectionService = mImpl != null && mImpl.mHotwordDetectionConnection != null; } IVoiceInteractionSoundTriggerSession session; if (forHotwordDetectionService) { // Use our own identity and handle the permission checks ourselves. This allows // properly checking/noting against the voice interactor or hotword detection // service as needed. if (HotwordDetectionConnection.DEBUG) { Slog.d(TAG, "Creating a SoundTriggerSession for a HotwordDetectionService"); } originatorIdentity.uid = Binder.getCallingUid(); originatorIdentity.pid = Binder.getCallingPid(); session = new SoundTriggerSessionPermissionsDecorator( createSoundTriggerSessionForSelfIdentity(client), mContext, originatorIdentity); } else { if (HotwordDetectionConnection.DEBUG) { Slog.d(TAG, "Creating a SoundTriggerSession"); } try (SafeCloseable ignored = PermissionUtil.establishIdentityDirect( originatorIdentity)) { SoundTriggerSession session = new SoundTriggerSession( mSoundTriggerInternal.attach(client)); synchronized (mSessions) { mSessions.add(new WeakReference<>(session)); session = new SoundTriggerSession(mSoundTriggerInternal.attach(client)); } } return session; return new SoundTriggerSessionBinderProxy(session); } private IVoiceInteractionSoundTriggerSession createSoundTriggerSessionForSelfIdentity( IBinder client) { Identity identity = new Identity(); identity.uid = Process.myUid(); identity.pid = Process.myPid(); identity.packageName = ActivityThread.currentOpPackageName(); return Binder.withCleanCallingIdentity(() -> { try (SafeCloseable ignored = IdentityContext.create(identity)) { return new SoundTriggerSession(mSoundTriggerInternal.attach(client)); } }); } // TODO: VI Make sure the caller is the current user or profile Loading Loading @@ -1334,7 +1367,11 @@ public class VoiceInteractionManagerService extends SystemService { return null; } class SoundTriggerSession extends IVoiceInteractionSoundTriggerSession.Stub { /** * Implementation of SoundTriggerSession. Does not implement {@link #asBinder()} as it's * intended to be wrapped by an {@link IVoiceInteractionSoundTriggerSession.Stub} object. */ private class SoundTriggerSession implements IVoiceInteractionSoundTriggerSession { final SoundTriggerInternal.Session mSession; private IHotwordRecognitionStatusCallback mSessionExternalCallback; private IRecognitionStatusCallback mSessionInternalCallback; Loading Loading @@ -1481,6 +1518,12 @@ public class VoiceInteractionManagerService extends SystemService { } } @Override public IBinder asBinder() { throw new UnsupportedOperationException( "This object isn't intended to be used as a Binder."); } private int unloadKeyphraseModel(int keyphraseId) { final long caller = Binder.clearCallingIdentity(); try { Loading Loading @@ -1709,22 +1752,6 @@ public class VoiceInteractionManagerService extends SystemService { } mSoundTriggerInternal.dump(fd, pw, args); // Dump all sessions. synchronized (mSessions) { ListIterator<WeakReference<SoundTriggerSession>> iter = mSessions.listIterator(); while (iter.hasNext()) { SoundTriggerSession session = iter.next().get(); if (session != null) { session.dump(fd, args); } else { // Session is obsolete, now is a good chance to remove it from the list. iter.remove(); } } } } @Override Loading Loading
services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java +1 −1 Original line number Diff line number Diff line Loading @@ -79,7 +79,7 @@ import java.util.function.Function; final class HotwordDetectionConnection { private static final String TAG = "HotwordDetectionConnection"; // TODO (b/177502877): Set the Debug flag to false before shipping. private static final boolean DEBUG = true; static final boolean DEBUG = true; // TODO: These constants need to be refined. private static final long VALIDATION_TIMEOUT_MILLIS = 3000; Loading
services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerSessionBinderProxy.java 0 → 100644 +72 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.voiceinteraction; import android.hardware.soundtrigger.SoundTrigger; import android.os.RemoteException; import com.android.internal.app.IHotwordRecognitionStatusCallback; import com.android.internal.app.IVoiceInteractionSoundTriggerSession; /** * A remote object that simply proxies calls to a real {@link IVoiceInteractionSoundTriggerSession} * implementation. This design pattern allows us to add decorators to the core implementation * (simply wrapping a binder object does not work). */ final class SoundTriggerSessionBinderProxy extends IVoiceInteractionSoundTriggerSession.Stub { private final IVoiceInteractionSoundTriggerSession mDelegate; SoundTriggerSessionBinderProxy(IVoiceInteractionSoundTriggerSession delegate) { mDelegate = delegate; } @Override public SoundTrigger.ModuleProperties getDspModuleProperties() throws RemoteException { return mDelegate.getDspModuleProperties(); } @Override public int startRecognition(int i, String s, IHotwordRecognitionStatusCallback iHotwordRecognitionStatusCallback, SoundTrigger.RecognitionConfig recognitionConfig, boolean b) throws RemoteException { return mDelegate.startRecognition(i, s, iHotwordRecognitionStatusCallback, recognitionConfig, b); } @Override public int stopRecognition(int i, IHotwordRecognitionStatusCallback iHotwordRecognitionStatusCallback) throws RemoteException { return mDelegate.stopRecognition(i, iHotwordRecognitionStatusCallback); } @Override public int setParameter(int i, int i1, int i2) throws RemoteException { return mDelegate.setParameter(i, i1, i2); } @Override public int getParameter(int i, int i1) throws RemoteException { return mDelegate.getParameter(i, i1); } @Override public SoundTrigger.ModelParamRange queryParameter(int i, int i1) throws RemoteException { return mDelegate.queryParameter(i, i1); } }
services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerSessionPermissionsDecorator.java 0 → 100644 +158 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.voiceinteraction; import static android.Manifest.permission.CAPTURE_AUDIO_HOTWORD; import static android.Manifest.permission.RECORD_AUDIO; import static com.android.server.voiceinteraction.HotwordDetectionConnection.DEBUG; import android.annotation.NonNull; import android.content.Context; import android.content.PermissionChecker; import android.hardware.soundtrigger.SoundTrigger; import android.media.permission.Identity; import android.media.permission.PermissionUtil; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.text.TextUtils; import android.util.Slog; import com.android.internal.app.IHotwordRecognitionStatusCallback; import com.android.internal.app.IVoiceInteractionSoundTriggerSession; /** * Decorates {@link IVoiceInteractionSoundTriggerSession} with permission checks for {@link * android.Manifest.permission#RECORD_AUDIO} and * {@link android.Manifest.permission#CAPTURE_AUDIO_HOTWORD}. * <p> * Does not implement {@link #asBinder()} as it's intended to be wrapped by an * {@link IVoiceInteractionSoundTriggerSession.Stub} object. */ final class SoundTriggerSessionPermissionsDecorator implements IVoiceInteractionSoundTriggerSession { static final String TAG = "SoundTriggerSessionPermissionsDecorator"; private final IVoiceInteractionSoundTriggerSession mDelegate; private final Context mContext; private final Identity mOriginatorIdentity; SoundTriggerSessionPermissionsDecorator(IVoiceInteractionSoundTriggerSession delegate, Context context, Identity originatorIdentity) { mDelegate = delegate; mContext = context; mOriginatorIdentity = originatorIdentity; } @Override public SoundTrigger.ModuleProperties getDspModuleProperties() throws RemoteException { // No permission needed. return mDelegate.getDspModuleProperties(); } @Override public int startRecognition(int i, String s, IHotwordRecognitionStatusCallback iHotwordRecognitionStatusCallback, SoundTrigger.RecognitionConfig recognitionConfig, boolean b) throws RemoteException { if (DEBUG) { Slog.d(TAG, "startRecognition"); } enforcePermissions(); return mDelegate.startRecognition(i, s, iHotwordRecognitionStatusCallback, recognitionConfig, b); } @Override public int stopRecognition(int i, IHotwordRecognitionStatusCallback iHotwordRecognitionStatusCallback) throws RemoteException { enforcePermissions(); return mDelegate.stopRecognition(i, iHotwordRecognitionStatusCallback); } @Override public int setParameter(int i, int i1, int i2) throws RemoteException { enforcePermissions(); return mDelegate.setParameter(i, i1, i2); } @Override public int getParameter(int i, int i1) throws RemoteException { enforcePermissions(); return mDelegate.getParameter(i, i1); } @Override public SoundTrigger.ModelParamRange queryParameter(int i, int i1) throws RemoteException { enforcePermissions(); return mDelegate.queryParameter(i, i1); } @Override public IBinder asBinder() { throw new UnsupportedOperationException( "This object isn't intended to be used as a Binder."); } // TODO: Share this code with SoundTriggerMiddlewarePermission. private void enforcePermissions() { enforcePermissionForPreflight(mContext, mOriginatorIdentity, RECORD_AUDIO); enforcePermissionForPreflight(mContext, mOriginatorIdentity, CAPTURE_AUDIO_HOTWORD); } /** * Throws a {@link SecurityException} if originator permanently doesn't have the given * permission, or a {@link ServiceSpecificException} with a {@link * #TEMPORARY_PERMISSION_DENIED} if caller originator doesn't have the given permission. * * @param context A {@link Context}, used for permission checks. * @param identity The identity to check. * @param permission The identifier of the permission we want to check. */ private static void enforcePermissionForPreflight(@NonNull Context context, @NonNull Identity identity, @NonNull String permission) { final int status = PermissionUtil.checkPermissionForPreflight(context, identity, permission); switch (status) { case PermissionChecker.PERMISSION_GRANTED: return; case PermissionChecker.PERMISSION_HARD_DENIED: throw new SecurityException( TextUtils.formatSimple("Failed to obtain permission %s for identity %s", permission, toString(identity))); case PermissionChecker.PERMISSION_SOFT_DENIED: throw new ServiceSpecificException(TEMPORARY_PERMISSION_DENIED, TextUtils.formatSimple("Failed to obtain permission %s for identity %s", permission, toString(identity))); default: throw new RuntimeException("Unexpected permission check result."); } } private static String toString(Identity identity) { return "{uid=" + identity.uid + " pid=" + identity.pid + " packageName=" + identity.packageName + " attributionTag=" + identity.attributionTag + "}"; } // Temporary hack for using the same status code as SoundTrigger, so we don't change behavior. // TODO: Reuse SoundTrigger code so we don't need to do this. private static final int TEMPORARY_PERMISSION_DENIED = 3; }
services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +55 −28 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.ActivityThread; import android.app.AppGlobals; import android.app.role.OnRoleHoldersChangedListener; import android.app.role.RoleManager; Loading Loading @@ -50,6 +51,7 @@ import android.hardware.soundtrigger.SoundTrigger.ModuleProperties; import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig; import android.media.AudioFormat; import android.media.permission.Identity; import android.media.permission.IdentityContext; import android.media.permission.PermissionUtil; import android.media.permission.SafeCloseable; import android.os.Binder; Loading @@ -59,6 +61,7 @@ import android.os.IBinder; import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; import android.os.Process; import android.os.RemoteCallback; import android.os.RemoteCallbackList; import android.os.RemoteException; Loading Loading @@ -104,11 +107,8 @@ import com.android.server.wm.ActivityTaskManagerInternal; import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Locale; import java.util.Objects; import java.util.concurrent.Executor; Loading Loading @@ -288,7 +288,6 @@ public class VoiceInteractionManagerService extends SystemService { private boolean mTemporarilyDisabled; private final boolean mEnableService; private final List<WeakReference<SoundTriggerSession>> mSessions = new LinkedList<>(); VoiceInteractionManagerServiceStub() { mEnableService = shouldEnableService(mContext); Loading @@ -299,15 +298,49 @@ public class VoiceInteractionManagerService extends SystemService { public @NonNull IVoiceInteractionSoundTriggerSession createSoundTriggerSessionAsOriginator( @NonNull Identity originatorIdentity, IBinder client) { Objects.requireNonNull(originatorIdentity); boolean forHotwordDetectionService; synchronized (VoiceInteractionManagerServiceStub.this) { enforceIsCurrentVoiceInteractionService(); forHotwordDetectionService = mImpl != null && mImpl.mHotwordDetectionConnection != null; } IVoiceInteractionSoundTriggerSession session; if (forHotwordDetectionService) { // Use our own identity and handle the permission checks ourselves. This allows // properly checking/noting against the voice interactor or hotword detection // service as needed. if (HotwordDetectionConnection.DEBUG) { Slog.d(TAG, "Creating a SoundTriggerSession for a HotwordDetectionService"); } originatorIdentity.uid = Binder.getCallingUid(); originatorIdentity.pid = Binder.getCallingPid(); session = new SoundTriggerSessionPermissionsDecorator( createSoundTriggerSessionForSelfIdentity(client), mContext, originatorIdentity); } else { if (HotwordDetectionConnection.DEBUG) { Slog.d(TAG, "Creating a SoundTriggerSession"); } try (SafeCloseable ignored = PermissionUtil.establishIdentityDirect( originatorIdentity)) { SoundTriggerSession session = new SoundTriggerSession( mSoundTriggerInternal.attach(client)); synchronized (mSessions) { mSessions.add(new WeakReference<>(session)); session = new SoundTriggerSession(mSoundTriggerInternal.attach(client)); } } return session; return new SoundTriggerSessionBinderProxy(session); } private IVoiceInteractionSoundTriggerSession createSoundTriggerSessionForSelfIdentity( IBinder client) { Identity identity = new Identity(); identity.uid = Process.myUid(); identity.pid = Process.myPid(); identity.packageName = ActivityThread.currentOpPackageName(); return Binder.withCleanCallingIdentity(() -> { try (SafeCloseable ignored = IdentityContext.create(identity)) { return new SoundTriggerSession(mSoundTriggerInternal.attach(client)); } }); } // TODO: VI Make sure the caller is the current user or profile Loading Loading @@ -1334,7 +1367,11 @@ public class VoiceInteractionManagerService extends SystemService { return null; } class SoundTriggerSession extends IVoiceInteractionSoundTriggerSession.Stub { /** * Implementation of SoundTriggerSession. Does not implement {@link #asBinder()} as it's * intended to be wrapped by an {@link IVoiceInteractionSoundTriggerSession.Stub} object. */ private class SoundTriggerSession implements IVoiceInteractionSoundTriggerSession { final SoundTriggerInternal.Session mSession; private IHotwordRecognitionStatusCallback mSessionExternalCallback; private IRecognitionStatusCallback mSessionInternalCallback; Loading Loading @@ -1481,6 +1518,12 @@ public class VoiceInteractionManagerService extends SystemService { } } @Override public IBinder asBinder() { throw new UnsupportedOperationException( "This object isn't intended to be used as a Binder."); } private int unloadKeyphraseModel(int keyphraseId) { final long caller = Binder.clearCallingIdentity(); try { Loading Loading @@ -1709,22 +1752,6 @@ public class VoiceInteractionManagerService extends SystemService { } mSoundTriggerInternal.dump(fd, pw, args); // Dump all sessions. synchronized (mSessions) { ListIterator<WeakReference<SoundTriggerSession>> iter = mSessions.listIterator(); while (iter.hasNext()) { SoundTriggerSession session = iter.next().get(); if (session != null) { session.dump(fd, args); } else { // Session is obsolete, now is a good chance to remove it from the list. iter.remove(); } } } } @Override Loading