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