Merge "Add AppOps listening to STService" into udc-dev
This commit is contained in:
@@ -119,6 +119,9 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
|
|||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
private SoundTriggerDeviceState mDeviceState = SoundTriggerDeviceState.DISABLE;
|
private SoundTriggerDeviceState mDeviceState = SoundTriggerDeviceState.DISABLE;
|
||||||
|
|
||||||
|
@GuardedBy("mLock")
|
||||||
|
private boolean mIsAppOpPermitted = true;
|
||||||
|
|
||||||
SoundTriggerHelper(Context context, EventLogger eventLogger,
|
SoundTriggerHelper(Context context, EventLogger eventLogger,
|
||||||
@NonNull Function<SoundTrigger.StatusListener, SoundTriggerModule> moduleProvider,
|
@NonNull Function<SoundTrigger.StatusListener, SoundTriggerModule> moduleProvider,
|
||||||
int moduleId,
|
int moduleId,
|
||||||
@@ -323,7 +326,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
|
|||||||
modelData.setRunInBatterySaverMode(runInBatterySaverMode);
|
modelData.setRunInBatterySaverMode(runInBatterySaverMode);
|
||||||
modelData.setSoundModel(soundModel);
|
modelData.setSoundModel(soundModel);
|
||||||
|
|
||||||
if (isRecognitionAllowedByDeviceState(modelData)) {
|
if (isRecognitionAllowed(modelData)) {
|
||||||
int startRecoResult = updateRecognitionLocked(modelData,
|
int startRecoResult = updateRecognitionLocked(modelData,
|
||||||
false /* Don't notify for synchronous calls */);
|
false /* Don't notify for synchronous calls */);
|
||||||
if (startRecoResult == SoundTrigger.STATUS_OK) {
|
if (startRecoResult == SoundTrigger.STATUS_OK) {
|
||||||
@@ -613,6 +616,16 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onAppOpStateChanged(boolean isPermitted) {
|
||||||
|
synchronized (mLock) {
|
||||||
|
if (mIsAppOpPermitted == isPermitted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mIsAppOpPermitted = isPermitted;
|
||||||
|
updateAllRecognitionsLocked();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public int getGenericModelState(UUID modelId) {
|
public int getGenericModelState(UUID modelId) {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
MetricsLogger.count(mContext, "sth_get_generic_model_state", 1);
|
MetricsLogger.count(mContext, "sth_get_generic_model_state", 1);
|
||||||
@@ -782,6 +795,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
|
|||||||
return event instanceof KeyphraseRecognitionEvent;
|
return event instanceof KeyphraseRecognitionEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GuardedBy("mLock")
|
||||||
private void onGenericRecognitionLocked(GenericRecognitionEvent event) {
|
private void onGenericRecognitionLocked(GenericRecognitionEvent event) {
|
||||||
MetricsLogger.count(mContext, "sth_generic_recognition_event", 1);
|
MetricsLogger.count(mContext, "sth_generic_recognition_event", 1);
|
||||||
if (event.status != SoundTrigger.RECOGNITION_STATUS_SUCCESS
|
if (event.status != SoundTrigger.RECOGNITION_STATUS_SUCCESS
|
||||||
@@ -866,6 +880,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GuardedBy("mLock")
|
||||||
private void onResourcesAvailableLocked() {
|
private void onResourcesAvailableLocked() {
|
||||||
mEventLogger.enqueue(new SessionEvent(Type.RESOURCES_AVAILABLE, null));
|
mEventLogger.enqueue(new SessionEvent(Type.RESOURCES_AVAILABLE, null));
|
||||||
updateAllRecognitionsLocked();
|
updateAllRecognitionsLocked();
|
||||||
@@ -911,6 +926,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
|
|||||||
return keyphraseExtras[0].id;
|
return keyphraseExtras[0].id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GuardedBy("mLock")
|
||||||
private void onKeyphraseRecognitionLocked(KeyphraseRecognitionEvent event) {
|
private void onKeyphraseRecognitionLocked(KeyphraseRecognitionEvent event) {
|
||||||
Slog.i(TAG, "Recognition success");
|
Slog.i(TAG, "Recognition success");
|
||||||
MetricsLogger.count(mContext, "sth_keyphrase_recognition_event", 1);
|
MetricsLogger.count(mContext, "sth_keyphrase_recognition_event", 1);
|
||||||
@@ -956,6 +972,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GuardedBy("mLock")
|
||||||
private void updateAllRecognitionsLocked() {
|
private void updateAllRecognitionsLocked() {
|
||||||
// updateRecognitionLocked can possibly update the list of models
|
// updateRecognitionLocked can possibly update the list of models
|
||||||
ArrayList<ModelData> modelDatas = new ArrayList<ModelData>(mModelDataMap.values());
|
ArrayList<ModelData> modelDatas = new ArrayList<ModelData>(mModelDataMap.values());
|
||||||
@@ -964,8 +981,9 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GuardedBy("mLock")
|
||||||
private int updateRecognitionLocked(ModelData model, boolean notifyClientOnError) {
|
private int updateRecognitionLocked(ModelData model, boolean notifyClientOnError) {
|
||||||
boolean shouldStartModel = model.isRequested() && isRecognitionAllowedByDeviceState(model);
|
boolean shouldStartModel = model.isRequested() && isRecognitionAllowed(model);
|
||||||
if (shouldStartModel == model.isModelStarted()) {
|
if (shouldStartModel == model.isModelStarted()) {
|
||||||
// No-op.
|
// No-op.
|
||||||
return STATUS_OK;
|
return STATUS_OK;
|
||||||
@@ -1184,7 +1202,10 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
|
|||||||
* @return True if recognition is allowed to run at this time. False if not.
|
* @return True if recognition is allowed to run at this time. False if not.
|
||||||
*/
|
*/
|
||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
private boolean isRecognitionAllowedByDeviceState(ModelData modelData) {
|
private boolean isRecognitionAllowed(ModelData modelData) {
|
||||||
|
if (!mIsAppOpPermitted) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return switch (mDeviceState) {
|
return switch (mDeviceState) {
|
||||||
case DISABLE -> false;
|
case DISABLE -> false;
|
||||||
case CRITICAL -> modelData.shouldRunInBatterySaverMode();
|
case CRITICAL -> modelData.shouldRunInBatterySaverMode();
|
||||||
@@ -1195,6 +1216,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
|
|||||||
|
|
||||||
// A single routine that implements the start recognition logic for both generic and keyphrase
|
// A single routine that implements the start recognition logic for both generic and keyphrase
|
||||||
// models.
|
// models.
|
||||||
|
@GuardedBy("mLock")
|
||||||
private int startRecognitionLocked(ModelData modelData, boolean notifyClientOnError) {
|
private int startRecognitionLocked(ModelData modelData, boolean notifyClientOnError) {
|
||||||
IRecognitionStatusCallback callback = modelData.getCallback();
|
IRecognitionStatusCallback callback = modelData.getCallback();
|
||||||
RecognitionConfig config = modelData.getRecognitionConfig();
|
RecognitionConfig config = modelData.getRecognitionConfig();
|
||||||
@@ -1205,7 +1227,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
|
|||||||
return STATUS_ERROR;
|
return STATUS_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isRecognitionAllowedByDeviceState(modelData)) {
|
if (!isRecognitionAllowed(modelData)) {
|
||||||
// Nothing to do here.
|
// Nothing to do here.
|
||||||
Slog.w(TAG, "startRecognition requested but not allowed.");
|
Slog.w(TAG, "startRecognition requested but not allowed.");
|
||||||
MetricsLogger.count(mContext, "sth_start_recognition_not_allowed", 1);
|
MetricsLogger.count(mContext, "sth_start_recognition_not_allowed", 1);
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ import android.Manifest;
|
|||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.app.ActivityThread;
|
import android.app.ActivityThread;
|
||||||
|
import android.app.AppOpsManager;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -111,6 +112,7 @@ import java.io.FileDescriptor;
|
|||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.function.Consumer;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
@@ -236,6 +238,8 @@ public class SoundTriggerService extends SystemService {
|
|||||||
private final DeviceStateHandler mDeviceStateHandler;
|
private final DeviceStateHandler mDeviceStateHandler;
|
||||||
private final Executor mDeviceStateHandlerExecutor = Executors.newSingleThreadExecutor();
|
private final Executor mDeviceStateHandlerExecutor = Executors.newSingleThreadExecutor();
|
||||||
private PhoneCallStateHandler mPhoneCallStateHandler;
|
private PhoneCallStateHandler mPhoneCallStateHandler;
|
||||||
|
private AppOpsManager mAppOpsManager;
|
||||||
|
private PackageManager mPackageManager;
|
||||||
|
|
||||||
public SoundTriggerService(Context context) {
|
public SoundTriggerService(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
@@ -258,6 +262,8 @@ public class SoundTriggerService extends SystemService {
|
|||||||
Slog.d(TAG, "onBootPhase: " + phase + " : " + isSafeMode());
|
Slog.d(TAG, "onBootPhase: " + phase + " : " + isSafeMode());
|
||||||
if (PHASE_THIRD_PARTY_APPS_CAN_START == phase) {
|
if (PHASE_THIRD_PARTY_APPS_CAN_START == phase) {
|
||||||
mDbHelper = new SoundTriggerDbHelper(mContext);
|
mDbHelper = new SoundTriggerDbHelper(mContext);
|
||||||
|
mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
|
||||||
|
mPackageManager = mContext.getPackageManager();
|
||||||
final PowerManager powerManager = mContext.getSystemService(PowerManager.class);
|
final PowerManager powerManager = mContext.getSystemService(PowerManager.class);
|
||||||
// Hook up power state listener
|
// Hook up power state listener
|
||||||
mContext.registerReceiver(
|
mContext.registerReceiver(
|
||||||
@@ -349,6 +355,44 @@ public class SoundTriggerService extends SystemService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class MyAppOpsListener implements AppOpsManager.OnOpChangedListener {
|
||||||
|
private final Identity mOriginatorIdentity;
|
||||||
|
private final Consumer<Boolean> mOnOpModeChanged;
|
||||||
|
|
||||||
|
MyAppOpsListener(Identity originatorIdentity, Consumer<Boolean> onOpModeChanged) {
|
||||||
|
mOriginatorIdentity = Objects.requireNonNull(originatorIdentity);
|
||||||
|
mOnOpModeChanged = Objects.requireNonNull(onOpModeChanged);
|
||||||
|
// Validate package name
|
||||||
|
try {
|
||||||
|
int uid = mPackageManager.getPackageUid(mOriginatorIdentity.packageName,
|
||||||
|
PackageManager.PackageInfoFlags.of(0));
|
||||||
|
if (uid != mOriginatorIdentity.uid) {
|
||||||
|
throw new SecurityException("Package name: " +
|
||||||
|
mOriginatorIdentity.packageName + "with uid: " + uid
|
||||||
|
+ "attempted to spoof as: " + mOriginatorIdentity.uid);
|
||||||
|
}
|
||||||
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
|
throw new SecurityException("Package name not found: "
|
||||||
|
+ mOriginatorIdentity.packageName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onOpChanged(String op, String packageName) {
|
||||||
|
if (!Objects.equals(op, AppOpsManager.OPSTR_RECORD_AUDIO)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final int mode = mAppOpsManager.checkOpNoThrow(
|
||||||
|
AppOpsManager.OPSTR_RECORD_AUDIO, mOriginatorIdentity.uid,
|
||||||
|
mOriginatorIdentity.packageName);
|
||||||
|
mOnOpModeChanged.accept(mode == AppOpsManager.MODE_ALLOWED);
|
||||||
|
}
|
||||||
|
|
||||||
|
void forceOpChangeRefresh() {
|
||||||
|
onOpChanged(AppOpsManager.OPSTR_RECORD_AUDIO, mOriginatorIdentity.packageName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class SoundTriggerServiceStub extends ISoundTriggerService.Stub {
|
class SoundTriggerServiceStub extends ISoundTriggerService.Stub {
|
||||||
@Override
|
@Override
|
||||||
public ISoundTriggerSession attachAsOriginator(@NonNull Identity originatorIdentity,
|
public ISoundTriggerSession attachAsOriginator(@NonNull Identity originatorIdentity,
|
||||||
@@ -463,6 +507,7 @@ public class SoundTriggerService extends SystemService {
|
|||||||
private final Object mCallbacksLock = new Object();
|
private final Object mCallbacksLock = new Object();
|
||||||
private final TreeMap<UUID, IRecognitionStatusCallback> mCallbacks = new TreeMap<>();
|
private final TreeMap<UUID, IRecognitionStatusCallback> mCallbacks = new TreeMap<>();
|
||||||
private final EventLogger mEventLogger;
|
private final EventLogger mEventLogger;
|
||||||
|
private final MyAppOpsListener mAppOpsListener;
|
||||||
|
|
||||||
SoundTriggerSessionStub(@NonNull IBinder client,
|
SoundTriggerSessionStub(@NonNull IBinder client,
|
||||||
SoundTriggerHelper soundTriggerHelper, EventLogger eventLogger) {
|
SoundTriggerHelper soundTriggerHelper, EventLogger eventLogger) {
|
||||||
@@ -479,6 +524,12 @@ public class SoundTriggerService extends SystemService {
|
|||||||
}
|
}
|
||||||
mListener = (SoundTriggerDeviceState state)
|
mListener = (SoundTriggerDeviceState state)
|
||||||
-> mSoundTriggerHelper.onDeviceStateChanged(state);
|
-> mSoundTriggerHelper.onDeviceStateChanged(state);
|
||||||
|
mAppOpsListener = new MyAppOpsListener(mOriginatorIdentity,
|
||||||
|
mSoundTriggerHelper::onAppOpStateChanged);
|
||||||
|
mAppOpsListener.forceOpChangeRefresh();
|
||||||
|
mAppOpsManager.startWatchingMode(AppOpsManager.OPSTR_RECORD_AUDIO,
|
||||||
|
mOriginatorIdentity.packageName, AppOpsManager.WATCH_FOREGROUND_CHANGES,
|
||||||
|
mAppOpsListener);
|
||||||
mDeviceStateHandler.registerListener(mListener);
|
mDeviceStateHandler.registerListener(mListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -930,6 +981,9 @@ public class SoundTriggerService extends SystemService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void detach() {
|
private void detach() {
|
||||||
|
if (mAppOpsListener != null) {
|
||||||
|
mAppOpsManager.stopWatchingMode(mAppOpsListener);
|
||||||
|
}
|
||||||
mDeviceStateHandler.unregisterListener(mListener);
|
mDeviceStateHandler.unregisterListener(mListener);
|
||||||
mSoundTriggerHelper.detach();
|
mSoundTriggerHelper.detach();
|
||||||
detachSessionLogger(mEventLogger);
|
detachSessionLogger(mEventLogger);
|
||||||
@@ -945,9 +999,8 @@ public class SoundTriggerService extends SystemService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void enforceDetectionPermissions(ComponentName detectionService) {
|
private void enforceDetectionPermissions(ComponentName detectionService) {
|
||||||
PackageManager packageManager = mContext.getPackageManager();
|
|
||||||
String packageName = detectionService.getPackageName();
|
String packageName = detectionService.getPackageName();
|
||||||
if (packageManager.checkPermission(
|
if (mPackageManager.checkPermission(
|
||||||
Manifest.permission.CAPTURE_AUDIO_HOTWORD, packageName)
|
Manifest.permission.CAPTURE_AUDIO_HOTWORD, packageName)
|
||||||
!= PackageManager.PERMISSION_GRANTED) {
|
!= PackageManager.PERMISSION_GRANTED) {
|
||||||
throw new SecurityException(detectionService.getPackageName() + " does not have"
|
throw new SecurityException(detectionService.getPackageName() + " does not have"
|
||||||
@@ -1635,6 +1688,7 @@ public class SoundTriggerService extends SystemService {
|
|||||||
private final EventLogger mEventLogger;
|
private final EventLogger mEventLogger;
|
||||||
private final Identity mOriginatorIdentity;
|
private final Identity mOriginatorIdentity;
|
||||||
private final @NonNull DeviceStateListener mListener;
|
private final @NonNull DeviceStateListener mListener;
|
||||||
|
private final MyAppOpsListener mAppOpsListener;
|
||||||
|
|
||||||
private final SparseArray<UUID> mModelUuid = new SparseArray<>(1);
|
private final SparseArray<UUID> mModelUuid = new SparseArray<>(1);
|
||||||
|
|
||||||
@@ -1655,6 +1709,12 @@ public class SoundTriggerService extends SystemService {
|
|||||||
}
|
}
|
||||||
mListener = (SoundTriggerDeviceState state)
|
mListener = (SoundTriggerDeviceState state)
|
||||||
-> mSoundTriggerHelper.onDeviceStateChanged(state);
|
-> mSoundTriggerHelper.onDeviceStateChanged(state);
|
||||||
|
mAppOpsListener = new MyAppOpsListener(mOriginatorIdentity,
|
||||||
|
mSoundTriggerHelper::onAppOpStateChanged);
|
||||||
|
mAppOpsListener.forceOpChangeRefresh();
|
||||||
|
mAppOpsManager.startWatchingMode(AppOpsManager.OPSTR_RECORD_AUDIO,
|
||||||
|
mOriginatorIdentity.packageName, AppOpsManager.WATCH_FOREGROUND_CHANGES,
|
||||||
|
mAppOpsListener);
|
||||||
mDeviceStateHandler.registerListener(mListener);
|
mDeviceStateHandler.registerListener(mListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1722,6 +1782,9 @@ public class SoundTriggerService extends SystemService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void detachInternal() {
|
private void detachInternal() {
|
||||||
|
if (mAppOpsListener != null) {
|
||||||
|
mAppOpsManager.stopWatchingMode(mAppOpsListener);
|
||||||
|
}
|
||||||
mEventLogger.enqueue(new SessionEvent(Type.DETACH, null));
|
mEventLogger.enqueue(new SessionEvent(Type.DETACH, null));
|
||||||
detachSessionLogger(mEventLogger);
|
detachSessionLogger(mEventLogger);
|
||||||
mDeviceStateHandler.unregisterListener(mListener);
|
mDeviceStateHandler.unregisterListener(mListener);
|
||||||
|
|||||||
Reference in New Issue
Block a user