diff --git a/core/java/android/hardware/ISensorPrivacyManager.aidl b/core/java/android/hardware/ISensorPrivacyManager.aidl index debc074ea21a3..d69a9e14b4bf2 100644 --- a/core/java/android/hardware/ISensorPrivacyManager.aidl +++ b/core/java/android/hardware/ISensorPrivacyManager.aidl @@ -48,4 +48,8 @@ interface ISensorPrivacyManager { void suppressIndividualSensorPrivacyReminders(int userId, int sensor, IBinder token, boolean suppress); + + void addUserGlobalIndividualSensorPrivacyListener(int sensor, in ISensorPrivacyListener listener); + + void removeUserGlobalIndividualSensorPrivacyListener(int sensor, in ISensorPrivacyListener listener); } \ No newline at end of file diff --git a/core/java/android/hardware/SensorPrivacyManager.java b/core/java/android/hardware/SensorPrivacyManager.java index b7d95e7dea745..4526ab770bc64 100644 --- a/core/java/android/hardware/SensorPrivacyManager.java +++ b/core/java/android/hardware/SensorPrivacyManager.java @@ -24,6 +24,7 @@ import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; import android.annotation.UserIdInt; +import android.app.ActivityManager; import android.content.Context; import android.os.Binder; import android.os.IBinder; @@ -247,8 +248,7 @@ public final class SensorPrivacyManager { @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(@Sensors.Sensor int sensor, @NonNull OnSensorPrivacyChangedListener listener) { - addSensorPrivacyListener(sensor, mContext.getUserId(), mContext.getMainExecutor(), - listener); + addSensorPrivacyListener(sensor, mContext.getMainExecutor(), listener); } /** @@ -283,7 +283,25 @@ public final class SensorPrivacyManager { @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(@Sensors.Sensor int sensor, @NonNull Executor executor, @NonNull OnSensorPrivacyChangedListener listener) { - addSensorPrivacyListener(sensor, mContext.getUserId(), executor, listener); + Pair key = new Pair<>(listener, sensor); + synchronized (mIndividualListeners) { + ISensorPrivacyListener iListener = mIndividualListeners.get(key); + if (iListener == null) { + iListener = new ISensorPrivacyListener.Stub() { + @Override + public void onSensorPrivacyChanged(boolean enabled) { + executor.execute(() -> listener.onSensorPrivacyChanged(sensor, enabled)); + } + }; + mIndividualListeners.put(key, iListener); + } + + try { + mService.addUserGlobalIndividualSensorPrivacyListener(sensor, iListener); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } /** @@ -361,7 +379,7 @@ public final class SensorPrivacyManager { @SystemApi @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY) public boolean isSensorPrivacyEnabled(@Sensors.Sensor int sensor) { - return isSensorPrivacyEnabled(sensor, mContext.getUserId()); + return isSensorPrivacyEnabled(sensor, getCurrentUserId()); } /** @@ -392,7 +410,7 @@ public final class SensorPrivacyManager { @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setSensorPrivacy(@Sources.Source int source, @Sensors.Sensor int sensor, boolean enable) { - setSensorPrivacy(source, sensor, enable, mContext.getUserId()); + setSensorPrivacy(source, sensor, enable, getCurrentUserId()); } /** @@ -428,7 +446,7 @@ public final class SensorPrivacyManager { @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setSensorPrivacyForProfileGroup(@Sources.Source int source, @Sensors.Sensor int sensor, boolean enable) { - setSensorPrivacyForProfileGroup(source , sensor, enable, mContext.getUserId()); + setSensorPrivacyForProfileGroup(source , sensor, enable, getCurrentUserId()); } /** @@ -463,7 +481,7 @@ public final class SensorPrivacyManager { @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY) public void suppressSensorPrivacyReminders(int sensor, boolean suppress) { - suppressSensorPrivacyReminders(sensor, suppress, mContext.getUserId()); + suppressSensorPrivacyReminders(sensor, suppress, getCurrentUserId()); } /** @@ -590,4 +608,13 @@ public final class SensorPrivacyManager { throw e.rethrowFromSystemServer(); } } + + private int getCurrentUserId() { + try { + return ActivityManager.getService().getCurrentUserId(); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + return 0; + } } diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java index ac43fbd4f976b..019e4ea207efc 100644 --- a/services/core/java/com/android/server/SensorPrivacyService.java +++ b/services/core/java/com/android/server/SensorPrivacyService.java @@ -232,14 +232,14 @@ public final class SensorPrivacyService extends SystemService { public void onUserStarting(TargetUser user) { if (mCurrentUser == -1) { mCurrentUser = user.getUserIdentifier(); - setGlobalRestriction(); + mSensorPrivacyServiceImpl.userSwitching(-1, user.getUserIdentifier()); } } @Override public void onUserSwitching(TargetUser from, TargetUser to) { mCurrentUser = to.getUserIdentifier(); - setGlobalRestriction(); + mSensorPrivacyServiceImpl.userSwitching(from.getUserIdentifier(), to.getUserIdentifier()); } class SensorPrivacyServiceImpl extends ISensorPrivacyManager.Stub implements @@ -897,18 +897,22 @@ public final class SensorPrivacyService extends SystemService { public boolean isIndividualSensorPrivacyEnabled(@UserIdInt int userId, int sensor) { enforceObserveSensorPrivacyPermission(); synchronized (mLock) { - SparseArray states = mIndividualEnabled.get(userId); - if (states == null) { - return false; - } - SensorState state = states.get(sensor); - if (state == null) { - return false; - } - return state.mEnabled; + return isIndividualSensorPrivacyEnabledLocked(userId, sensor); } } + private boolean isIndividualSensorPrivacyEnabledLocked(int userId, int sensor) { + SparseArray states = mIndividualEnabled.get(userId); + if (states == null) { + return false; + } + SensorState state = states.get(sensor); + if (state == null) { + return false; + } + return state.mEnabled; + } + /** * Returns the state of sensor privacy from persistent storage. */ @@ -1149,11 +1153,27 @@ public final class SensorPrivacyService extends SystemService { ISensorPrivacyListener listener) { enforceObserveSensorPrivacyPermission(); if (listener == null) { - throw new NullPointerException("listener cannot be null"); + throw new IllegalArgumentException("listener cannot be null"); } mHandler.addListener(userId, sensor, listener); } + + /** + * Registers a listener to be notified when the sensor privacy state changes. The callback + * can be called if the user changes and the setting is different between the transitioning + * users. + */ + @Override + public void addUserGlobalIndividualSensorPrivacyListener(int sensor, + ISensorPrivacyListener listener) { + enforceObserveSensorPrivacyPermission(); + if (listener == null) { + throw new IllegalArgumentException("listener cannot be null"); + } + mHandler.addUserGlobalListener(sensor, listener); + } + /** * Unregisters a listener from sensor privacy state change notifications. */ @@ -1174,11 +1194,21 @@ public final class SensorPrivacyService extends SystemService { ISensorPrivacyListener listener) { enforceObserveSensorPrivacyPermission(); if (listener == null) { - throw new NullPointerException("listener cannot be null"); + throw new IllegalArgumentException("listener cannot be null"); } mHandler.removeListener(sensor, listener); } + @Override + public void removeUserGlobalIndividualSensorPrivacyListener(int sensor, + ISensorPrivacyListener listener) { + enforceObserveSensorPrivacyPermission(); + if (listener == null) { + throw new IllegalArgumentException("listener cannot be null"); + } + mHandler.removeUserGlobalListener(sensor, listener); + } + @Override public void suppressIndividualSensorPrivacyReminders(int userId, int sensor, IBinder token, boolean suppress) { @@ -1209,6 +1239,40 @@ public final class SensorPrivacyService extends SystemService { } } + private void userSwitching(int from, int to) { + boolean micState; + boolean camState; + boolean prevMicState; + boolean prevCamState; + synchronized (mLock) { + prevMicState = isIndividualSensorPrivacyEnabledLocked(from, MICROPHONE); + prevCamState = isIndividualSensorPrivacyEnabledLocked(from, CAMERA); + micState = isIndividualSensorPrivacyEnabledLocked(to, MICROPHONE); + camState = isIndividualSensorPrivacyEnabledLocked(to, CAMERA); + } + if (prevMicState != micState) { + mHandler.onUserGlobalSensorPrivacyChanged(MICROPHONE, micState); + setGlobalRestriction(MICROPHONE, micState); + } + if (prevCamState != camState) { + mHandler.onUserGlobalSensorPrivacyChanged(CAMERA, camState); + setGlobalRestriction(CAMERA, micState); + } + } + + private void setGlobalRestriction(int sensor, boolean enabled) { + switch(sensor) { + case MICROPHONE: + mAppOpsManagerInternal.setGlobalRestriction(OP_RECORD_AUDIO, enabled, + mAppOpsRestrictionToken); + break; + case CAMERA: + mAppOpsManagerInternal.setGlobalRestriction(OP_CAMERA, enabled, + mAppOpsRestrictionToken); + break; + } + } + /** * Remove a sensor use reminder suppression token. * @@ -1454,7 +1518,12 @@ public final class SensorPrivacyService extends SystemService { @GuardedBy("mListenerLock") private final SparseArray>> mIndividualSensorListeners = new SparseArray<>(); - private final ArrayMap mDeathRecipients; + @GuardedBy("mListenerLock") + private final SparseArray> + mUserGlobalIndividualSensorListeners = new SparseArray<>(); + @GuardedBy("mListenerLock") + private final ArrayMap> + mDeathRecipients; private final Context mContext; SensorPrivacyHandler(Looper looper, Context context) { @@ -1479,18 +1548,22 @@ public final class SensorPrivacyService extends SystemService { mSensorPrivacyServiceImpl)); } + public void onUserGlobalSensorPrivacyChanged(int sensor, boolean enabled) { + sendMessage(PooledLambda.obtainMessage( + SensorPrivacyHandler::handleUserGlobalSensorPrivacyChanged, + this, sensor, enabled)); + } + public void addListener(ISensorPrivacyListener listener) { synchronized (mListenerLock) { - DeathRecipient deathRecipient = new DeathRecipient(listener); - mDeathRecipients.put(listener, deathRecipient); - mListeners.register(listener); + if (mListeners.register(listener)) { + addDeathRecipient(listener); + } } } public void addListener(int userId, int sensor, ISensorPrivacyListener listener) { synchronized (mListenerLock) { - DeathRecipient deathRecipient = new DeathRecipient(listener); - mDeathRecipients.put(listener, deathRecipient); SparseArray> listenersForUser = mIndividualSensorListeners.get(userId); if (listenersForUser == null) { @@ -1502,32 +1575,55 @@ public final class SensorPrivacyService extends SystemService { listeners = new RemoteCallbackList<>(); listenersForUser.put(sensor, listeners); } - listeners.register(listener); + if (listeners.register(listener)) { + addDeathRecipient(listener); + } + } + } + + public void addUserGlobalListener(int sensor, ISensorPrivacyListener listener) { + synchronized (mListenerLock) { + RemoteCallbackList listeners = + mUserGlobalIndividualSensorListeners.get(sensor); + if (listeners == null) { + listeners = new RemoteCallbackList<>(); + mUserGlobalIndividualSensorListeners.put(sensor, listeners); + } + if (listeners.register(listener)) { + addDeathRecipient(listener); + } } } public void removeListener(ISensorPrivacyListener listener) { synchronized (mListenerLock) { - DeathRecipient deathRecipient = mDeathRecipients.remove(listener); - if (deathRecipient != null) { - deathRecipient.destroy(); + if (mListeners.unregister(listener)) { + removeDeathRecipient(listener); } - mListeners.unregister(listener); } } public void removeListener(int sensor, ISensorPrivacyListener listener) { synchronized (mListenerLock) { - DeathRecipient deathRecipient = mDeathRecipients.remove(listener); - if (deathRecipient != null) { - deathRecipient.destroy(); - } - for (int i = 0, numUsers = mIndividualSensorListeners.size(); i < numUsers; i++) { RemoteCallbackList callbacks = mIndividualSensorListeners.valueAt(i).get(sensor); if (callbacks != null) { - callbacks.unregister(listener); + if (callbacks.unregister(listener)) { + removeDeathRecipient(listener); + } + } + } + } + } + + public void removeUserGlobalListener(int sensor, ISensorPrivacyListener listener) { + synchronized (mListenerLock) { + RemoteCallbackList callbacks = + mUserGlobalIndividualSensorListeners.get(sensor); + if (callbacks != null) { + if (callbacks.unregister(listener)) { + removeDeathRecipient(listener); } } } @@ -1551,9 +1647,12 @@ public final class SensorPrivacyService extends SystemService { SparseArray> listenersForUser = mIndividualSensorListeners.get(userId); - setGlobalRestriction(); if (userId == mCurrentUser) { - setGlobalRestriction(); + mSensorPrivacyServiceImpl.setGlobalRestriction(sensor, enabled); + } + + if (userId == mCurrentUser) { + onUserGlobalSensorPrivacyChanged(sensor, enabled); } if (listenersForUser == null) { @@ -1563,16 +1662,42 @@ public final class SensorPrivacyService extends SystemService { if (listeners == null) { return; } - final int count = listeners.beginBroadcast(); - for (int i = 0; i < count; i++) { - ISensorPrivacyListener listener = listeners.getBroadcastItem(i); - try { - listener.onSensorPrivacyChanged(enabled); - } catch (RemoteException e) { - Log.e(TAG, "Caught an exception notifying listener " + listener + ": ", e); + try { + final int count = listeners.beginBroadcast(); + for (int i = 0; i < count; i++) { + ISensorPrivacyListener listener = listeners.getBroadcastItem(i); + try { + listener.onSensorPrivacyChanged(enabled); + } catch (RemoteException e) { + Log.e(TAG, "Caught an exception notifying listener " + listener + ": ", e); + } } + } finally { + listeners.finishBroadcast(); + } + } + + public void handleUserGlobalSensorPrivacyChanged(int sensor, boolean enabled) { + RemoteCallbackList listeners = + mUserGlobalIndividualSensorListeners.get(sensor); + + if (listeners == null) { + return; + } + + try { + final int count = listeners.beginBroadcast(); + for (int i = 0; i < count; i++) { + ISensorPrivacyListener listener = listeners.getBroadcastItem(i); + try { + listener.onSensorPrivacyChanged(enabled); + } catch (RemoteException e) { + Log.e(TAG, "Caught an exception notifying listener " + listener + ": ", e); + } + } + } finally { + listeners.finishBroadcast(); } - listeners.finishBroadcast(); } public void removeSuppressPackageReminderToken(Pair key, @@ -1581,20 +1706,33 @@ public final class SensorPrivacyService extends SystemService { SensorPrivacyServiceImpl::removeSuppressPackageReminderToken, mSensorPrivacyServiceImpl, key, token)); } - } - private void setGlobalRestriction() { - boolean camState = - mSensorPrivacyServiceImpl - .isIndividualSensorPrivacyEnabled(mCurrentUser, CAMERA); - boolean micState = - mSensorPrivacyServiceImpl - .isIndividualSensorPrivacyEnabled(mCurrentUser, MICROPHONE); + private void addDeathRecipient(ISensorPrivacyListener listener) { + Pair deathRecipient = mDeathRecipients.get(listener); + if (deathRecipient == null) { + deathRecipient = new Pair<>(new DeathRecipient(listener), 1); + } else { + int newRefCount = deathRecipient.second + 1; + deathRecipient = new Pair<>(deathRecipient.first, newRefCount); + } + mDeathRecipients.put(listener, deathRecipient); + } - mAppOpsManagerInternal - .setGlobalRestriction(OP_CAMERA, camState, mAppOpsRestrictionToken); - mAppOpsManagerInternal - .setGlobalRestriction(OP_RECORD_AUDIO, micState, mAppOpsRestrictionToken); + private void removeDeathRecipient(ISensorPrivacyListener listener) { + Pair deathRecipient = mDeathRecipients.get(listener); + if (deathRecipient == null) { + return; + } else { + int newRefCount = deathRecipient.second - 1; + if (newRefCount == 0) { + mDeathRecipients.remove(listener); + deathRecipient.first.destroy(); + return; + } + deathRecipient = new Pair<>(deathRecipient.first, newRefCount); + } + mDeathRecipients.put(listener, deathRecipient); + } } private final class DeathRecipient implements IBinder.DeathRecipient {