/* * 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.car.settings.privacy; import android.Manifest; import android.car.drivingstate.CarUxRestrictions; import android.content.Context; import android.content.pm.PackageManager; import android.hardware.SensorPrivacyManager; import android.os.UserHandle; import android.os.UserManager; import android.permission.PermissionControllerManager; import androidx.preference.Preference; import com.android.car.settings.R; import com.android.car.settings.common.FragmentController; import com.android.car.settings.common.Logger; import com.android.car.settings.common.PreferenceController; import com.android.internal.annotations.VisibleForTesting; import com.android.settingslib.utils.StringUtil; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; /** * This controller displays the number of non-system apps that have access to microphone. */ public class ManageMicPermissionsPreferenceController extends PreferenceController { private static final Logger LOG = new Logger( ManageMicPermissionsPreferenceController.class); private final AtomicInteger mLoadingInProgress = new AtomicInteger(0); private int mNumTotal = 0; private int mNumHasAccess = 0; private final SensorPrivacyManager mSensorPrivacyManager; private final UserManager mUserManager; private final SensorPrivacyManager.OnSensorPrivacyChangedListener mListener = (sensor, enabled) -> refreshUi(); public ManageMicPermissionsPreferenceController(Context context, String preferenceKey, FragmentController fragmentController, CarUxRestrictions uxRestrictions) { this(context, preferenceKey, fragmentController, uxRestrictions, SensorPrivacyManager.getInstance(context), context.getSystemService(UserManager.class)); } @VisibleForTesting ManageMicPermissionsPreferenceController(Context context, String preferenceKey, FragmentController fragmentController, CarUxRestrictions uxRestrictions, SensorPrivacyManager sensorPrivacyManager, UserManager userManager) { super(context, preferenceKey, fragmentController, uxRestrictions); mSensorPrivacyManager = sensorPrivacyManager; mUserManager = userManager; } @Override protected Class getPreferenceType() { return Preference.class; } @Override protected void onStartInternal() { mSensorPrivacyManager.addSensorPrivacyListener( SensorPrivacyManager.Sensors.MICROPHONE, mListener); } @Override protected void onStopInternal() { mSensorPrivacyManager.removeSensorPrivacyListener(SensorPrivacyManager.Sensors.MICROPHONE, mListener); } @Override public void updateState(Preference preference) { super.updateState(preference); if (mSensorPrivacyManager.isSensorPrivacyEnabled( SensorPrivacyManager.Sensors.MICROPHONE)) { getPreference().setSummary(getContext().getString( R.string.microphone_app_permission_summary_microphone_off)); return; } // Bail out if there's another loading request in progress. if (mLoadingInProgress.get() != 0) { return; } getPreference().setSummary( getContext().getString(R.string.microphone_settings_loading_app_permission_stats)); mNumTotal = 0; mNumHasAccess = 0; // Retrieve a list of users inside the current user profile group. List users = mUserManager.getUserProfiles(); // need to asynchronously load 2 callbacks for each profile mLoadingInProgress.set(2 * users.size()); for (UserHandle user : users) { Context userContext = createPackageContextAsUser(getContext(), user); if (userContext == null) { // remove the two callbacks that we would normally expect for this user if (mLoadingInProgress.addAndGet(-2) == 0) { setAppCounts(mNumTotal, mNumHasAccess); } continue; } PermissionControllerManager permController = userContext.getSystemService(PermissionControllerManager.class); permController.countPermissionApps( Collections.singletonList(Manifest.permission.RECORD_AUDIO), /* flags= */ 0, (numApps) -> { mNumTotal += numApps; if (mLoadingInProgress.decrementAndGet() == 0) { setAppCounts(mNumTotal, mNumHasAccess); } }, /* handler= */ null); permController.countPermissionApps( Collections.singletonList(Manifest.permission.RECORD_AUDIO), PermissionControllerManager.COUNT_ONLY_WHEN_GRANTED, (numApps) -> { mNumHasAccess += numApps; if (mLoadingInProgress.decrementAndGet() == 0) { setAppCounts(mNumTotal, mNumHasAccess); } }, /* handler= */ null); } } private void setAppCounts(int numTotal, int numHasAccess) { Map arguments = new HashMap<>(); arguments.put("count", numHasAccess); arguments.put("total_count", numTotal); getPreference().setSummary(StringUtil.getIcuPluralsString(getContext(), arguments, R.string.microphone_app_permission_summary_microphone_on)); } /** * Returns a context created from the given context for the given user, or null if it fails. */ private Context createPackageContextAsUser(Context context, UserHandle userHandle) { try { return context.createPackageContextAsUser( context.getPackageName(), /* flags= */ 0, userHandle); } catch (PackageManager.NameNotFoundException e) { LOG.e("Failed to create user context", e); } return null; } }