1 package com.android.settings.location; 2 3 import static android.Manifest.permission.ACCESS_COARSE_LOCATION; 4 import static android.Manifest.permission.ACCESS_FINE_LOCATION; 5 6 import android.content.Context; 7 import android.location.LocationManager; 8 import android.os.UserHandle; 9 import android.os.UserManager; 10 import android.permission.PermissionControllerManager; 11 import android.provider.Settings; 12 13 import androidx.annotation.VisibleForTesting; 14 import androidx.preference.Preference; 15 16 import com.android.settings.R; 17 import com.android.settings.Utils; 18 import com.android.settings.core.PreferenceControllerMixin; 19 import com.android.settingslib.core.lifecycle.Lifecycle; 20 21 import java.util.Arrays; 22 import java.util.List; 23 import java.util.concurrent.atomic.AtomicInteger; 24 25 public class AppLocationPermissionPreferenceController extends 26 LocationBasePreferenceController implements PreferenceControllerMixin { 27 28 private static final String KEY_APP_LEVEL_PERMISSIONS = "app_level_permissions"; 29 /** Total number of apps that has location permission. */ 30 @VisibleForTesting 31 int mNumTotal = -1; 32 /** Total number of apps that has background location permission. */ 33 @VisibleForTesting 34 int mNumHasLocation = -1; 35 36 final AtomicInteger loadingInProgress = new AtomicInteger(0); 37 private int mNumTotalLoading = 0; 38 private int mNumHasLocationLoading = 0; 39 40 private final LocationManager mLocationManager; 41 private Preference mPreference; 42 AppLocationPermissionPreferenceController(Context context, Lifecycle lifecycle)43 public AppLocationPermissionPreferenceController(Context context, Lifecycle lifecycle) { 44 super(context, lifecycle); 45 mLocationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); 46 } 47 48 @Override getPreferenceKey()49 public String getPreferenceKey() { 50 return KEY_APP_LEVEL_PERMISSIONS; 51 } 52 53 @Override isAvailable()54 public boolean isAvailable() { 55 return Settings.Global.getInt(mContext.getContentResolver(), 56 Settings.Global.LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED, 1) == 1; 57 } 58 59 @Override getSummary()60 public CharSequence getSummary() { 61 if (mLocationManager.isLocationEnabled()) { 62 if (mNumTotal == -1 || mNumHasLocation == -1) { 63 return mContext.getString(R.string.location_settings_loading_app_permission_stats); 64 } 65 return mContext.getResources().getQuantityString( 66 R.plurals.location_app_permission_summary_location_on, mNumHasLocation, 67 mNumHasLocation, mNumTotal); 68 } else { 69 return mContext.getString(R.string.location_app_permission_summary_location_off); 70 } 71 } 72 setAppCounts(int numTotal, int numHasLocation)73 private void setAppCounts(int numTotal, int numHasLocation) { 74 mNumTotal = numTotal; 75 mNumHasLocation = numHasLocation; 76 refreshSummary(mPreference); 77 } 78 79 @Override updateState(Preference preference)80 public void updateState(Preference preference) { 81 super.updateState(preference); 82 mPreference = preference; 83 refreshSummary(preference); 84 // Bail out if location has been disabled, or there's another loading request in progress. 85 if (!mLocationManager.isLocationEnabled() || 86 loadingInProgress.get() != 0) { 87 return; 88 } 89 mNumTotalLoading = 0; 90 mNumHasLocationLoading = 0; 91 // Retrieve a list of users inside the current user profile group. 92 final List<UserHandle> users = mContext.getSystemService( 93 UserManager.class).getUserProfiles(); 94 loadingInProgress.set(2 * users.size()); 95 for (UserHandle user : users) { 96 final Context userContext = Utils.createPackageContextAsUser(mContext, 97 user.getIdentifier()); 98 if (userContext == null) { 99 for (int i = 0; i < 2; ++i) { 100 if (loadingInProgress.decrementAndGet() == 0) { 101 setAppCounts(mNumTotalLoading, mNumHasLocationLoading); 102 } 103 } 104 continue; 105 } 106 final PermissionControllerManager permController = 107 userContext.getSystemService(PermissionControllerManager.class); 108 permController.countPermissionApps( 109 Arrays.asList(ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION), 0, 110 (numApps) -> { 111 mNumTotalLoading += numApps; 112 if (loadingInProgress.decrementAndGet() == 0) { 113 setAppCounts(mNumTotalLoading, mNumHasLocationLoading); 114 } 115 }, null); 116 permController.countPermissionApps( 117 Arrays.asList(ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION), 118 PermissionControllerManager.COUNT_ONLY_WHEN_GRANTED, 119 (numApps) -> { 120 mNumHasLocationLoading += numApps; 121 if (loadingInProgress.decrementAndGet() == 0) { 122 setAppCounts(mNumTotalLoading, mNumHasLocationLoading); 123 } 124 }, null); 125 } 126 } 127 128 @Override onLocationModeChanged(int mode, boolean restricted)129 public void onLocationModeChanged(int mode, boolean restricted) { 130 if (mPreference != null) { 131 updateState(mPreference); 132 } 133 } 134 } 135