Loading core/java/android/hardware/lights/Light.java +6 −0 Original line number Diff line number Diff line Loading @@ -38,6 +38,12 @@ public final class Light implements Parcelable { /** Type for lights that indicate microphone usage */ public static final int LIGHT_TYPE_MICROPHONE = 8; /** Type for lights that indicate camera usage * * @hide */ public static final int LIGHT_TYPE_CAMERA = 9; // These enum values start from 10001 to avoid collision with expanding of HAL light types. /** * Type for lights that indicate a monochrome color LED light. Loading core/res/res/values/colors.xml +3 −0 Original line number Diff line number Diff line Loading @@ -448,4 +448,7 @@ <color name="accessibility_magnification_background">#F50D60</color> <color name="accessibility_daltonizer_background">#00BCD4</color> <color name="accessibility_color_inversion_background">#546E7A</color> <!-- Color of camera light when camera is in use --> <color name="camera_privacy_light">#FFFFFF</color> </resources> core/res/res/values/symbols.xml +2 −0 Original line number Diff line number Diff line Loading @@ -4722,4 +4722,6 @@ <java-symbol type="bool" name="config_lowPowerStandbySupported" /> <java-symbol type="bool" name="config_lowPowerStandbyEnabledByDefault" /> <java-symbol type="integer" name="config_lowPowerStandbyNonInteractiveTimeout" /> <java-symbol type="color" name="camera_privacy_light"/> </resources> services/core/java/com/android/server/sensorprivacy/CameraPrivacyLightController.java 0 → 100644 +125 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 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.sensorprivacy; import android.app.AppOpsManager; import android.content.Context; import android.hardware.lights.Light; import android.hardware.lights.LightState; import android.hardware.lights.LightsManager; import android.hardware.lights.LightsRequest; import android.permission.PermissionManager; import android.util.ArraySet; import com.android.internal.R; import com.android.server.FgThread; import java.util.ArrayList; import java.util.List; import java.util.Set; class CameraPrivacyLightController implements AppOpsManager.OnOpActiveChangedListener { private final Context mContext; private final LightsManager mLightsManager; private final Set<String> mActivePackages = new ArraySet<>(); private final Set<String> mActivePhonePackages = new ArraySet<>(); private final int mCameraPrivacyLightColor; private final List<Light> mCameraLights = new ArrayList<>(); private final AppOpsManager mAppOpsManager; private LightsManager.LightsSession mLightsSession = null; CameraPrivacyLightController(Context context) { mContext = context; mAppOpsManager = mContext.getSystemService(AppOpsManager.class); mLightsManager = mContext.getSystemService(LightsManager.class); mCameraPrivacyLightColor = mContext.getColor(R.color.camera_privacy_light); List<Light> lights = mLightsManager.getLights(); for (int i = 0; i < lights.size(); i++) { Light light = lights.get(i); if (light.getType() == Light.LIGHT_TYPE_CAMERA) { mCameraLights.add(light); } } if (mCameraLights.isEmpty()) { return; } mAppOpsManager.startWatchingActive( new String[] {AppOpsManager.OPSTR_CAMERA, AppOpsManager.OPSTR_PHONE_CALL_CAMERA}, FgThread.getExecutor(), this); } @Override public void onOpActiveChanged(String op, int uid, String packageName, boolean active) { final Set<String> activePackages; if (AppOpsManager.OPSTR_CAMERA.equals(op)) { activePackages = mActivePackages; } else if (AppOpsManager.OPSTR_PHONE_CALL_CAMERA.equals(op)) { activePackages = mActivePhonePackages; } else { return; } if (active) { activePackages.add(packageName); } else { activePackages.remove(packageName); } updateLightSession(); } private void updateLightSession() { Set<String> exemptedPackages = PermissionManager.getIndicatorExemptedPackages(mContext); boolean shouldSessionEnd = exemptedPackages.containsAll(mActivePackages) && exemptedPackages.containsAll(mActivePhonePackages); if (shouldSessionEnd) { if (mLightsSession == null) { return; } mLightsSession.close(); mLightsSession = null; } else { if (mLightsSession != null) { return; } LightsRequest.Builder requestBuilder = new LightsRequest.Builder(); for (int i = 0; i < mCameraLights.size(); i++) { requestBuilder.addLight(mCameraLights.get(i), new LightState.Builder() .setColor(mCameraPrivacyLightColor) .build()); } mLightsSession = mLightsManager.openSession(Integer.MAX_VALUE); mLightsSession.requestLights(requestBuilder.build()); } } } services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java +4 −0 Original line number Diff line number Diff line Loading @@ -154,6 +154,8 @@ public final class SensorPrivacyService extends SystemService { private final AppOpsManagerInternal mAppOpsManagerInternal; private final TelephonyManager mTelephonyManager; private CameraPrivacyLightController mCameraPrivacyLightController; private final IBinder mAppOpsRestrictionToken = new Binder(); private SensorPrivacyManagerInternalImpl mSensorPrivacyManagerInternal; Loading Loading @@ -190,6 +192,8 @@ public final class SensorPrivacyService extends SystemService { if (phase == PHASE_SYSTEM_SERVICES_READY) { mKeyguardManager = mContext.getSystemService(KeyguardManager.class); mEmergencyCallHelper = new EmergencyCallHelper(); } else if (phase == PHASE_ACTIVITY_MANAGER_READY) { mCameraPrivacyLightController = new CameraPrivacyLightController(mContext); } } Loading Loading
core/java/android/hardware/lights/Light.java +6 −0 Original line number Diff line number Diff line Loading @@ -38,6 +38,12 @@ public final class Light implements Parcelable { /** Type for lights that indicate microphone usage */ public static final int LIGHT_TYPE_MICROPHONE = 8; /** Type for lights that indicate camera usage * * @hide */ public static final int LIGHT_TYPE_CAMERA = 9; // These enum values start from 10001 to avoid collision with expanding of HAL light types. /** * Type for lights that indicate a monochrome color LED light. Loading
core/res/res/values/colors.xml +3 −0 Original line number Diff line number Diff line Loading @@ -448,4 +448,7 @@ <color name="accessibility_magnification_background">#F50D60</color> <color name="accessibility_daltonizer_background">#00BCD4</color> <color name="accessibility_color_inversion_background">#546E7A</color> <!-- Color of camera light when camera is in use --> <color name="camera_privacy_light">#FFFFFF</color> </resources>
core/res/res/values/symbols.xml +2 −0 Original line number Diff line number Diff line Loading @@ -4722,4 +4722,6 @@ <java-symbol type="bool" name="config_lowPowerStandbySupported" /> <java-symbol type="bool" name="config_lowPowerStandbyEnabledByDefault" /> <java-symbol type="integer" name="config_lowPowerStandbyNonInteractiveTimeout" /> <java-symbol type="color" name="camera_privacy_light"/> </resources>
services/core/java/com/android/server/sensorprivacy/CameraPrivacyLightController.java 0 → 100644 +125 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 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.sensorprivacy; import android.app.AppOpsManager; import android.content.Context; import android.hardware.lights.Light; import android.hardware.lights.LightState; import android.hardware.lights.LightsManager; import android.hardware.lights.LightsRequest; import android.permission.PermissionManager; import android.util.ArraySet; import com.android.internal.R; import com.android.server.FgThread; import java.util.ArrayList; import java.util.List; import java.util.Set; class CameraPrivacyLightController implements AppOpsManager.OnOpActiveChangedListener { private final Context mContext; private final LightsManager mLightsManager; private final Set<String> mActivePackages = new ArraySet<>(); private final Set<String> mActivePhonePackages = new ArraySet<>(); private final int mCameraPrivacyLightColor; private final List<Light> mCameraLights = new ArrayList<>(); private final AppOpsManager mAppOpsManager; private LightsManager.LightsSession mLightsSession = null; CameraPrivacyLightController(Context context) { mContext = context; mAppOpsManager = mContext.getSystemService(AppOpsManager.class); mLightsManager = mContext.getSystemService(LightsManager.class); mCameraPrivacyLightColor = mContext.getColor(R.color.camera_privacy_light); List<Light> lights = mLightsManager.getLights(); for (int i = 0; i < lights.size(); i++) { Light light = lights.get(i); if (light.getType() == Light.LIGHT_TYPE_CAMERA) { mCameraLights.add(light); } } if (mCameraLights.isEmpty()) { return; } mAppOpsManager.startWatchingActive( new String[] {AppOpsManager.OPSTR_CAMERA, AppOpsManager.OPSTR_PHONE_CALL_CAMERA}, FgThread.getExecutor(), this); } @Override public void onOpActiveChanged(String op, int uid, String packageName, boolean active) { final Set<String> activePackages; if (AppOpsManager.OPSTR_CAMERA.equals(op)) { activePackages = mActivePackages; } else if (AppOpsManager.OPSTR_PHONE_CALL_CAMERA.equals(op)) { activePackages = mActivePhonePackages; } else { return; } if (active) { activePackages.add(packageName); } else { activePackages.remove(packageName); } updateLightSession(); } private void updateLightSession() { Set<String> exemptedPackages = PermissionManager.getIndicatorExemptedPackages(mContext); boolean shouldSessionEnd = exemptedPackages.containsAll(mActivePackages) && exemptedPackages.containsAll(mActivePhonePackages); if (shouldSessionEnd) { if (mLightsSession == null) { return; } mLightsSession.close(); mLightsSession = null; } else { if (mLightsSession != null) { return; } LightsRequest.Builder requestBuilder = new LightsRequest.Builder(); for (int i = 0; i < mCameraLights.size(); i++) { requestBuilder.addLight(mCameraLights.get(i), new LightState.Builder() .setColor(mCameraPrivacyLightColor) .build()); } mLightsSession = mLightsManager.openSession(Integer.MAX_VALUE); mLightsSession.requestLights(requestBuilder.build()); } } }
services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java +4 −0 Original line number Diff line number Diff line Loading @@ -154,6 +154,8 @@ public final class SensorPrivacyService extends SystemService { private final AppOpsManagerInternal mAppOpsManagerInternal; private final TelephonyManager mTelephonyManager; private CameraPrivacyLightController mCameraPrivacyLightController; private final IBinder mAppOpsRestrictionToken = new Binder(); private SensorPrivacyManagerInternalImpl mSensorPrivacyManagerInternal; Loading Loading @@ -190,6 +192,8 @@ public final class SensorPrivacyService extends SystemService { if (phase == PHASE_SYSTEM_SERVICES_READY) { mKeyguardManager = mContext.getSystemService(KeyguardManager.class); mEmergencyCallHelper = new EmergencyCallHelper(); } else if (phase == PHASE_ACTIVITY_MANAGER_READY) { mCameraPrivacyLightController = new CameraPrivacyLightController(mContext); } } Loading