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.content.Intent; 8 import android.location.LocationManager; 9 import android.os.UserHandle; 10 import android.os.UserManager; 11 import android.permission.PermissionControllerManager; 12 import android.provider.Settings; 13 import android.text.TextUtils; 14 15 import androidx.annotation.NonNull; 16 import androidx.annotation.VisibleForTesting; 17 import androidx.preference.Preference; 18 import androidx.preference.PreferenceScreen; 19 20 import com.android.settings.R; 21 import com.android.settings.Utils; 22 import com.android.settings.core.PreferenceControllerMixin; 23 import com.android.settingslib.utils.StringUtil; 24 25 import java.util.Arrays; 26 import java.util.HashMap; 27 import java.util.List; 28 import java.util.Map; 29 import java.util.concurrent.atomic.AtomicInteger; 30 31 public class AppLocationPermissionPreferenceController extends 32 LocationBasePreferenceController implements PreferenceControllerMixin { 33 34 /** Total number of apps that has location permission. */ 35 @VisibleForTesting 36 int mNumTotal = -1; 37 /** Total number of apps that has background location permission. */ 38 @VisibleForTesting 39 int mNumHasLocation = -1; 40 41 final AtomicInteger loadingInProgress = new AtomicInteger(0); 42 private int mNumTotalLoading = 0; 43 private int mNumHasLocationLoading = 0; 44 45 private final LocationManager mLocationManager; 46 private Preference mPreference; 47 AppLocationPermissionPreferenceController(Context context, String key)48 public AppLocationPermissionPreferenceController(Context context, String key) { 49 super(context, key); 50 mLocationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); 51 } 52 53 @Override getAvailabilityStatus()54 public int getAvailabilityStatus() { 55 return Settings.Global.getInt(mContext.getContentResolver(), 56 Settings.Global.LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED, 1) == 1 ? AVAILABLE 57 : UNSUPPORTED_ON_DEVICE; 58 } 59 60 @Override getSummary()61 public CharSequence getSummary() { 62 if (mLocationManager.isLocationEnabled()) { 63 if (mNumTotal == -1 || mNumHasLocation == -1) { 64 return mContext.getString(R.string.location_settings_loading_app_permission_stats); 65 } 66 Map<String, Object> arguments = new HashMap<>(); 67 arguments.put("count", mNumHasLocation); 68 arguments.put("total", mNumTotal); 69 return StringUtil.getIcuPluralsString(mContext, arguments, 70 R.string.location_app_permission_summary_location_on); 71 } else { 72 return mContext.getString(R.string.location_app_permission_summary_location_off); 73 } 74 } 75 76 @Override displayPreference(@onNull PreferenceScreen screen)77 public void displayPreference(@NonNull PreferenceScreen screen) { 78 super.displayPreference(screen); 79 80 Preference pref = screen.findPreference(getPreferenceKey()); 81 if (pref != null) { 82 pref.setIntent(new Intent(Intent.ACTION_MANAGE_PERMISSION_APPS) 83 .setPackage(mContext.getPackageManager().getPermissionControllerPackageName()) 84 .putExtra(TextUtils.equals(pref.getKey(), "app_level_permissions") 85 ? Intent.EXTRA_PERMISSION_NAME 86 : Intent.EXTRA_PERMISSION_GROUP_NAME, 87 "android.permission-group.LOCATION")); 88 } 89 } 90 setAppCounts(int numTotal, int numHasLocation)91 private void setAppCounts(int numTotal, int numHasLocation) { 92 mNumTotal = numTotal; 93 mNumHasLocation = numHasLocation; 94 refreshSummary(mPreference); 95 } 96 97 @Override updateState(Preference preference)98 public void updateState(Preference preference) { 99 super.updateState(preference); 100 mPreference = preference; 101 refreshSummary(preference); 102 // Bail out if location has been disabled, or there's another loading request in progress. 103 if (!mLocationManager.isLocationEnabled() || 104 loadingInProgress.get() != 0) { 105 return; 106 } 107 mNumTotalLoading = 0; 108 mNumHasLocationLoading = 0; 109 // Retrieve a list of users inside the current user profile group. 110 final List<UserHandle> users = mContext.getSystemService( 111 UserManager.class).getUserProfiles(); 112 loadingInProgress.set(2 * users.size()); 113 for (UserHandle user : users) { 114 final Context userContext = Utils.createPackageContextAsUser(mContext, 115 user.getIdentifier()); 116 if (userContext == null) { 117 for (int i = 0; i < 2; ++i) { 118 if (loadingInProgress.decrementAndGet() == 0) { 119 setAppCounts(mNumTotalLoading, mNumHasLocationLoading); 120 } 121 } 122 continue; 123 } 124 final PermissionControllerManager permController = 125 userContext.getSystemService(PermissionControllerManager.class); 126 permController.countPermissionApps( 127 Arrays.asList(ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION), 0, 128 (numApps) -> { 129 mNumTotalLoading += numApps; 130 if (loadingInProgress.decrementAndGet() == 0) { 131 setAppCounts(mNumTotalLoading, mNumHasLocationLoading); 132 } 133 }, null); 134 permController.countPermissionApps( 135 Arrays.asList(ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION), 136 PermissionControllerManager.COUNT_ONLY_WHEN_GRANTED, 137 (numApps) -> { 138 mNumHasLocationLoading += numApps; 139 if (loadingInProgress.decrementAndGet() == 0) { 140 setAppCounts(mNumTotalLoading, mNumHasLocationLoading); 141 } 142 }, null); 143 } 144 } 145 146 @Override onLocationModeChanged(int mode, boolean restricted)147 public void onLocationModeChanged(int mode, boolean restricted) { 148 if (mPreference != null) { 149 updateState(mPreference); 150 } 151 } 152 } 153