1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.permissioncontroller.permission.utils; 18 19 import static android.Manifest.permission_group.ACTIVITY_RECOGNITION; 20 import static android.Manifest.permission_group.CALENDAR; 21 import static android.Manifest.permission_group.CALL_LOG; 22 import static android.Manifest.permission_group.CAMERA; 23 import static android.Manifest.permission_group.CONTACTS; 24 import static android.Manifest.permission_group.LOCATION; 25 import static android.Manifest.permission_group.MICROPHONE; 26 import static android.Manifest.permission_group.NEARBY_DEVICES; 27 import static android.Manifest.permission_group.NOTIFICATIONS; 28 import static android.Manifest.permission_group.PHONE; 29 import static android.Manifest.permission_group.READ_MEDIA_AURAL; 30 import static android.Manifest.permission_group.READ_MEDIA_VISUAL; 31 import static android.Manifest.permission_group.SENSORS; 32 import static android.Manifest.permission_group.SMS; 33 import static android.Manifest.permission_group.STORAGE; 34 import static android.app.AppOpsManager.MODE_ALLOWED; 35 import static android.app.AppOpsManager.OPSTR_LEGACY_STORAGE; 36 import static android.content.Context.MODE_PRIVATE; 37 import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT; 38 import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT; 39 import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; 40 import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED; 41 import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED; 42 import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; 43 import static android.os.UserHandle.myUserId; 44 45 import static com.android.permissioncontroller.Constants.INVALID_SESSION_ID; 46 47 import static java.lang.annotation.RetentionPolicy.SOURCE; 48 49 import android.Manifest; 50 import android.app.AppOpsManager; 51 import android.app.Application; 52 import android.app.admin.DevicePolicyManager; 53 import android.app.role.RoleManager; 54 import android.content.ActivityNotFoundException; 55 import android.content.Context; 56 import android.content.Intent; 57 import android.content.SharedPreferences; 58 import android.content.pm.ApplicationInfo; 59 import android.content.pm.PackageInfo; 60 import android.content.pm.PackageItemInfo; 61 import android.content.pm.PackageManager; 62 import android.content.pm.PackageManager.NameNotFoundException; 63 import android.content.pm.PermissionGroupInfo; 64 import android.content.pm.PermissionInfo; 65 import android.content.pm.ResolveInfo; 66 import android.content.res.Resources; 67 import android.content.res.Resources.Theme; 68 import android.graphics.drawable.Drawable; 69 import android.hardware.SensorPrivacyManager; 70 import android.os.Build; 71 import android.os.Parcelable; 72 import android.os.Process; 73 import android.os.UserHandle; 74 import android.os.UserManager; 75 import android.provider.DeviceConfig; 76 import android.provider.Settings; 77 import android.text.Html; 78 import android.text.TextUtils; 79 import android.text.format.DateFormat; 80 import android.util.ArrayMap; 81 import android.util.ArraySet; 82 import android.util.Log; 83 import android.util.TypedValue; 84 import android.view.Menu; 85 import android.view.MenuItem; 86 87 import androidx.annotation.IntDef; 88 import androidx.annotation.NonNull; 89 import androidx.annotation.Nullable; 90 import androidx.annotation.RequiresApi; 91 import androidx.annotation.StringRes; 92 import androidx.core.text.BidiFormatter; 93 import androidx.core.util.Preconditions; 94 95 import com.android.launcher3.icons.IconFactory; 96 import com.android.modules.utils.build.SdkLevel; 97 import com.android.permissioncontroller.Constants; 98 import com.android.permissioncontroller.DeviceUtils; 99 import com.android.permissioncontroller.PermissionControllerApplication; 100 import com.android.permissioncontroller.R; 101 import com.android.permissioncontroller.permission.model.AppPermissionGroup; 102 import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup; 103 import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo; 104 105 import java.lang.annotation.Retention; 106 import java.time.ZonedDateTime; 107 import java.time.temporal.ChronoUnit; 108 import java.util.ArrayList; 109 import java.util.Calendar; 110 import java.util.Collections; 111 import java.util.HashSet; 112 import java.util.List; 113 import java.util.Locale; 114 import java.util.Random; 115 import java.util.Set; 116 117 import kotlin.Triple; 118 119 public final class Utils { 120 121 @Retention(SOURCE) 122 @IntDef(value = {LAST_24H_SENSOR_TODAY, LAST_24H_SENSOR_YESTERDAY, 123 LAST_24H_CONTENT_PROVIDER, NOT_IN_LAST_7D}) 124 public @interface AppPermsLastAccessType {} 125 public static final int LAST_24H_SENSOR_TODAY = 1; 126 public static final int LAST_24H_SENSOR_YESTERDAY = 2; 127 public static final int LAST_24H_CONTENT_PROVIDER = 3; 128 public static final int LAST_7D_SENSOR = 4; 129 public static final int LAST_7D_CONTENT_PROVIDER = 5; 130 public static final int NOT_IN_LAST_7D = 6; 131 132 private static final List<String> SENSOR_DATA_PERMISSIONS = List.of( 133 Manifest.permission_group.LOCATION, 134 Manifest.permission_group.CAMERA, 135 Manifest.permission_group.MICROPHONE 136 ); 137 138 public static final List<String> STORAGE_SUPERGROUP_PERMISSIONS = 139 !SdkLevel.isAtLeastT() ? List.of() : List.of( 140 Manifest.permission_group.STORAGE, 141 Manifest.permission_group.READ_MEDIA_AURAL, 142 Manifest.permission_group.READ_MEDIA_VISUAL 143 ); 144 145 private static final String LOG_TAG = "Utils"; 146 147 public static final String OS_PKG = "android"; 148 149 public static final float DEFAULT_MAX_LABEL_SIZE_PX = 500f; 150 151 /** The time an app needs to be unused in order to be hibernated */ 152 public static final String PROPERTY_HIBERNATION_UNUSED_THRESHOLD_MILLIS = 153 "auto_revoke_unused_threshold_millis2"; 154 155 /** The frequency of running the job for hibernating apps */ 156 public static final String PROPERTY_HIBERNATION_CHECK_FREQUENCY_MILLIS = 157 "auto_revoke_check_frequency_millis"; 158 159 /** Whether hibernation targets apps that target a pre-S SDK */ 160 public static final String PROPERTY_HIBERNATION_TARGETS_PRE_S_APPS = 161 "app_hibernation_targets_pre_s_apps"; 162 163 /** Whether or not app hibernation is enabled on the device **/ 164 public static final String PROPERTY_APP_HIBERNATION_ENABLED = "app_hibernation_enabled"; 165 166 /** Whether to show the Permissions Hub. */ 167 private static final String PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_enabled"; 168 169 /** The timeout for one-time permissions */ 170 private static final String PROPERTY_ONE_TIME_PERMISSIONS_TIMEOUT_MILLIS = 171 "one_time_permissions_timeout_millis"; 172 173 /** The delay before ending a one-time permission session when all processes are dead */ 174 private static final String PROPERTY_ONE_TIME_PERMISSIONS_KILLED_DELAY_MILLIS = 175 "one_time_permissions_killed_delay_millis"; 176 177 /** Whether to show location access check notifications. */ 178 private static final String PROPERTY_LOCATION_ACCESS_CHECK_ENABLED = 179 "location_access_check_enabled"; 180 181 /** How frequently to check permission event store to scrub old data */ 182 public static final String PROPERTY_PERMISSION_EVENTS_CHECK_OLD_FREQUENCY_MILLIS = 183 "permission_events_check_old_frequency_millis"; 184 185 /** The time an app needs to be unused in order to be hibernated */ 186 public static final String PROPERTY_PERMISSION_DECISIONS_MAX_DATA_AGE_MILLIS = 187 "permission_decisions_max_data_age_millis"; 188 189 /** Whether or not warning banner is displayed when device sensors are off **/ 190 public static final String PROPERTY_WARNING_BANNER_DISPLAY_ENABLED = "warning_banner_enabled"; 191 192 /** All permission whitelists. */ 193 public static final int FLAGS_PERMISSION_WHITELIST_ALL = 194 PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM 195 | PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE 196 | PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER; 197 198 /** All permission restriction exemptions. */ 199 public static final int FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT = 200 FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT 201 | FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT 202 | FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT; 203 204 /** 205 * The default length of the timeout for one-time permissions 206 */ 207 public static final long ONE_TIME_PERMISSIONS_TIMEOUT_MILLIS = 1 * 60 * 1000; // 1 minute 208 209 /** 210 * The default length to wait before ending a one-time permission session after all processes 211 * are dead. 212 */ 213 public static final long ONE_TIME_PERMISSIONS_KILLED_DELAY_MILLIS = 5 * 1000; 214 215 /** Mapping permission -> group for all dangerous platform permissions */ 216 private static final ArrayMap<String, String> PLATFORM_PERMISSIONS; 217 218 /** Mapping group -> permissions for all dangerous platform permissions */ 219 private static final ArrayMap<String, ArrayList<String>> PLATFORM_PERMISSION_GROUPS; 220 221 /** Set of groups that will be able to receive one-time grant */ 222 private static final ArraySet<String> ONE_TIME_PERMISSION_GROUPS; 223 224 private static final ArrayMap<String, Integer> PERM_GROUP_REQUEST_RES; 225 private static final ArrayMap<String, Integer> PERM_GROUP_REQUEST_DETAIL_RES; 226 private static final ArrayMap<String, Integer> PERM_GROUP_BACKGROUND_REQUEST_RES; 227 private static final ArrayMap<String, Integer> PERM_GROUP_BACKGROUND_REQUEST_DETAIL_RES; 228 private static final ArrayMap<String, Integer> PERM_GROUP_UPGRADE_REQUEST_RES; 229 private static final ArrayMap<String, Integer> PERM_GROUP_UPGRADE_REQUEST_DETAIL_RES; 230 231 /** Permission -> Sensor codes */ 232 private static final ArrayMap<String, Integer> PERM_SENSOR_CODES; 233 /** Permission -> Icon res id */ 234 private static final ArrayMap<String, Integer> PERM_BLOCKED_ICON; 235 /** Permission -> Title res id */ 236 private static final ArrayMap<String, Integer> PERM_BLOCKED_TITLE; 237 238 public static final int FLAGS_ALWAYS_USER_SENSITIVE = 239 FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED 240 | FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED; 241 242 private static final String SYSTEM_PKG = "android"; 243 244 private static final String SYSTEM_AMBIENT_AUDIO_INTELLIGENCE = 245 "android.app.role.SYSTEM_AMBIENT_AUDIO_INTELLIGENCE"; 246 private static final String SYSTEM_UI_INTELLIGENCE = 247 "android.app.role.SYSTEM_UI_INTELLIGENCE"; 248 private static final String SYSTEM_AUDIO_INTELLIGENCE = 249 "android.app.role.SYSTEM_AUDIO_INTELLIGENCE"; 250 private static final String SYSTEM_NOTIFICATION_INTELLIGENCE = 251 "android.app.role.SYSTEM_NOTIFICATION_INTELLIGENCE"; 252 private static final String SYSTEM_TEXT_INTELLIGENCE = 253 "android.app.role.SYSTEM_TEXT_INTELLIGENCE"; 254 private static final String SYSTEM_VISUAL_INTELLIGENCE = 255 "android.app.role.SYSTEM_VISUAL_INTELLIGENCE"; 256 257 // TODO: theianchen Using hardcoded values here as a WIP solution for now. 258 private static final String[] EXEMPTED_ROLES = { 259 SYSTEM_AMBIENT_AUDIO_INTELLIGENCE, 260 SYSTEM_UI_INTELLIGENCE, 261 SYSTEM_AUDIO_INTELLIGENCE, 262 SYSTEM_NOTIFICATION_INTELLIGENCE, 263 SYSTEM_TEXT_INTELLIGENCE, 264 SYSTEM_VISUAL_INTELLIGENCE, 265 }; 266 267 static { 268 PLATFORM_PERMISSIONS = new ArrayMap<>(); 269 PLATFORM_PERMISSIONS.put(Manifest.permission.READ_CONTACTS, CONTACTS)270 PLATFORM_PERMISSIONS.put(Manifest.permission.READ_CONTACTS, CONTACTS); PLATFORM_PERMISSIONS.put(Manifest.permission.WRITE_CONTACTS, CONTACTS)271 PLATFORM_PERMISSIONS.put(Manifest.permission.WRITE_CONTACTS, CONTACTS); PLATFORM_PERMISSIONS.put(Manifest.permission.GET_ACCOUNTS, CONTACTS)272 PLATFORM_PERMISSIONS.put(Manifest.permission.GET_ACCOUNTS, CONTACTS); 273 PLATFORM_PERMISSIONS.put(Manifest.permission.READ_CALENDAR, CALENDAR)274 PLATFORM_PERMISSIONS.put(Manifest.permission.READ_CALENDAR, CALENDAR); PLATFORM_PERMISSIONS.put(Manifest.permission.WRITE_CALENDAR, CALENDAR)275 PLATFORM_PERMISSIONS.put(Manifest.permission.WRITE_CALENDAR, CALENDAR); 276 PLATFORM_PERMISSIONS.put(Manifest.permission.SEND_SMS, SMS)277 PLATFORM_PERMISSIONS.put(Manifest.permission.SEND_SMS, SMS); PLATFORM_PERMISSIONS.put(Manifest.permission.RECEIVE_SMS, SMS)278 PLATFORM_PERMISSIONS.put(Manifest.permission.RECEIVE_SMS, SMS); PLATFORM_PERMISSIONS.put(Manifest.permission.READ_SMS, SMS)279 PLATFORM_PERMISSIONS.put(Manifest.permission.READ_SMS, SMS); PLATFORM_PERMISSIONS.put(Manifest.permission.RECEIVE_MMS, SMS)280 PLATFORM_PERMISSIONS.put(Manifest.permission.RECEIVE_MMS, SMS); PLATFORM_PERMISSIONS.put(Manifest.permission.RECEIVE_WAP_PUSH, SMS)281 PLATFORM_PERMISSIONS.put(Manifest.permission.RECEIVE_WAP_PUSH, SMS); PLATFORM_PERMISSIONS.put(Manifest.permission.READ_CELL_BROADCASTS, SMS)282 PLATFORM_PERMISSIONS.put(Manifest.permission.READ_CELL_BROADCASTS, SMS); 283 284 // If permissions are added to the Storage group, they must be added to the 285 // STORAGE_PERMISSIONS list in PermissionManagerService in frameworks/base PLATFORM_PERMISSIONS.put(Manifest.permission.READ_EXTERNAL_STORAGE, STORAGE)286 PLATFORM_PERMISSIONS.put(Manifest.permission.READ_EXTERNAL_STORAGE, STORAGE); PLATFORM_PERMISSIONS.put(Manifest.permission.WRITE_EXTERNAL_STORAGE, STORAGE)287 PLATFORM_PERMISSIONS.put(Manifest.permission.WRITE_EXTERNAL_STORAGE, STORAGE); 288 if (!SdkLevel.isAtLeastT()) { PLATFORM_PERMISSIONS.put(Manifest.permission.ACCESS_MEDIA_LOCATION, STORAGE)289 PLATFORM_PERMISSIONS.put(Manifest.permission.ACCESS_MEDIA_LOCATION, STORAGE); 290 } 291 292 if (SdkLevel.isAtLeastT()) { PLATFORM_PERMISSIONS.put(Manifest.permission.READ_MEDIA_AUDIO, READ_MEDIA_AURAL)293 PLATFORM_PERMISSIONS.put(Manifest.permission.READ_MEDIA_AUDIO, READ_MEDIA_AURAL); PLATFORM_PERMISSIONS.put(Manifest.permission.READ_MEDIA_IMAGES, READ_MEDIA_VISUAL)294 PLATFORM_PERMISSIONS.put(Manifest.permission.READ_MEDIA_IMAGES, READ_MEDIA_VISUAL); PLATFORM_PERMISSIONS.put(Manifest.permission.READ_MEDIA_VIDEO, READ_MEDIA_VISUAL)295 PLATFORM_PERMISSIONS.put(Manifest.permission.READ_MEDIA_VIDEO, READ_MEDIA_VISUAL); PLATFORM_PERMISSIONS.put(Manifest.permission.ACCESS_MEDIA_LOCATION, READ_MEDIA_VISUAL)296 PLATFORM_PERMISSIONS.put(Manifest.permission.ACCESS_MEDIA_LOCATION, READ_MEDIA_VISUAL); 297 } 298 PLATFORM_PERMISSIONS.put(Manifest.permission.ACCESS_FINE_LOCATION, LOCATION)299 PLATFORM_PERMISSIONS.put(Manifest.permission.ACCESS_FINE_LOCATION, LOCATION); PLATFORM_PERMISSIONS.put(Manifest.permission.ACCESS_COARSE_LOCATION, LOCATION)300 PLATFORM_PERMISSIONS.put(Manifest.permission.ACCESS_COARSE_LOCATION, LOCATION); PLATFORM_PERMISSIONS.put(Manifest.permission.ACCESS_BACKGROUND_LOCATION, LOCATION)301 PLATFORM_PERMISSIONS.put(Manifest.permission.ACCESS_BACKGROUND_LOCATION, LOCATION); 302 303 if (SdkLevel.isAtLeastS()) { PLATFORM_PERMISSIONS.put(Manifest.permission.BLUETOOTH_ADVERTISE, NEARBY_DEVICES)304 PLATFORM_PERMISSIONS.put(Manifest.permission.BLUETOOTH_ADVERTISE, NEARBY_DEVICES); PLATFORM_PERMISSIONS.put(Manifest.permission.BLUETOOTH_CONNECT, NEARBY_DEVICES)305 PLATFORM_PERMISSIONS.put(Manifest.permission.BLUETOOTH_CONNECT, NEARBY_DEVICES); PLATFORM_PERMISSIONS.put(Manifest.permission.BLUETOOTH_SCAN, NEARBY_DEVICES)306 PLATFORM_PERMISSIONS.put(Manifest.permission.BLUETOOTH_SCAN, NEARBY_DEVICES); PLATFORM_PERMISSIONS.put(Manifest.permission.UWB_RANGING, NEARBY_DEVICES)307 PLATFORM_PERMISSIONS.put(Manifest.permission.UWB_RANGING, NEARBY_DEVICES); 308 } 309 if (SdkLevel.isAtLeastT()) { PLATFORM_PERMISSIONS.put(Manifest.permission.NEARBY_WIFI_DEVICES, NEARBY_DEVICES)310 PLATFORM_PERMISSIONS.put(Manifest.permission.NEARBY_WIFI_DEVICES, NEARBY_DEVICES); 311 } 312 PLATFORM_PERMISSIONS.put(Manifest.permission.READ_CALL_LOG, CALL_LOG)313 PLATFORM_PERMISSIONS.put(Manifest.permission.READ_CALL_LOG, CALL_LOG); PLATFORM_PERMISSIONS.put(Manifest.permission.WRITE_CALL_LOG, CALL_LOG)314 PLATFORM_PERMISSIONS.put(Manifest.permission.WRITE_CALL_LOG, CALL_LOG); PLATFORM_PERMISSIONS.put(Manifest.permission.PROCESS_OUTGOING_CALLS, CALL_LOG)315 PLATFORM_PERMISSIONS.put(Manifest.permission.PROCESS_OUTGOING_CALLS, CALL_LOG); 316 PLATFORM_PERMISSIONS.put(Manifest.permission.READ_PHONE_STATE, PHONE)317 PLATFORM_PERMISSIONS.put(Manifest.permission.READ_PHONE_STATE, PHONE); PLATFORM_PERMISSIONS.put(Manifest.permission.READ_PHONE_NUMBERS, PHONE)318 PLATFORM_PERMISSIONS.put(Manifest.permission.READ_PHONE_NUMBERS, PHONE); PLATFORM_PERMISSIONS.put(Manifest.permission.CALL_PHONE, PHONE)319 PLATFORM_PERMISSIONS.put(Manifest.permission.CALL_PHONE, PHONE); PLATFORM_PERMISSIONS.put(Manifest.permission.ADD_VOICEMAIL, PHONE)320 PLATFORM_PERMISSIONS.put(Manifest.permission.ADD_VOICEMAIL, PHONE); PLATFORM_PERMISSIONS.put(Manifest.permission.USE_SIP, PHONE)321 PLATFORM_PERMISSIONS.put(Manifest.permission.USE_SIP, PHONE); PLATFORM_PERMISSIONS.put(Manifest.permission.ANSWER_PHONE_CALLS, PHONE)322 PLATFORM_PERMISSIONS.put(Manifest.permission.ANSWER_PHONE_CALLS, PHONE); PLATFORM_PERMISSIONS.put(Manifest.permission.ACCEPT_HANDOVER, PHONE)323 PLATFORM_PERMISSIONS.put(Manifest.permission.ACCEPT_HANDOVER, PHONE); 324 PLATFORM_PERMISSIONS.put(Manifest.permission.RECORD_AUDIO, MICROPHONE)325 PLATFORM_PERMISSIONS.put(Manifest.permission.RECORD_AUDIO, MICROPHONE); 326 if (SdkLevel.isAtLeastS()) { PLATFORM_PERMISSIONS.put(Manifest.permission.RECORD_BACKGROUND_AUDIO, MICROPHONE)327 PLATFORM_PERMISSIONS.put(Manifest.permission.RECORD_BACKGROUND_AUDIO, MICROPHONE); 328 } 329 PLATFORM_PERMISSIONS.put(Manifest.permission.ACTIVITY_RECOGNITION, ACTIVITY_RECOGNITION)330 PLATFORM_PERMISSIONS.put(Manifest.permission.ACTIVITY_RECOGNITION, ACTIVITY_RECOGNITION); 331 PLATFORM_PERMISSIONS.put(Manifest.permission.CAMERA, CAMERA)332 PLATFORM_PERMISSIONS.put(Manifest.permission.CAMERA, CAMERA); 333 if (SdkLevel.isAtLeastS()) { PLATFORM_PERMISSIONS.put(Manifest.permission.BACKGROUND_CAMERA, CAMERA)334 PLATFORM_PERMISSIONS.put(Manifest.permission.BACKGROUND_CAMERA, CAMERA); 335 } 336 PLATFORM_PERMISSIONS.put(Manifest.permission.BODY_SENSORS, SENSORS)337 PLATFORM_PERMISSIONS.put(Manifest.permission.BODY_SENSORS, SENSORS); 338 339 if (SdkLevel.isAtLeastT()) { PLATFORM_PERMISSIONS.put(Manifest.permission.POST_NOTIFICATIONS, NOTIFICATIONS)340 PLATFORM_PERMISSIONS.put(Manifest.permission.POST_NOTIFICATIONS, NOTIFICATIONS); PLATFORM_PERMISSIONS.put(Manifest.permission.BODY_SENSORS_BACKGROUND, SENSORS)341 PLATFORM_PERMISSIONS.put(Manifest.permission.BODY_SENSORS_BACKGROUND, SENSORS); 342 } 343 344 PLATFORM_PERMISSION_GROUPS = new ArrayMap<>(); 345 int numPlatformPermissions = PLATFORM_PERMISSIONS.size(); 346 for (int i = 0; i < numPlatformPermissions; i++) { 347 String permission = PLATFORM_PERMISSIONS.keyAt(i); 348 String permissionGroup = PLATFORM_PERMISSIONS.valueAt(i); 349 350 ArrayList<String> permissionsOfThisGroup = PLATFORM_PERMISSION_GROUPS.get( 351 permissionGroup); 352 if (permissionsOfThisGroup == null) { 353 permissionsOfThisGroup = new ArrayList<>(); PLATFORM_PERMISSION_GROUPS.put(permissionGroup, permissionsOfThisGroup)354 PLATFORM_PERMISSION_GROUPS.put(permissionGroup, permissionsOfThisGroup); 355 } 356 357 permissionsOfThisGroup.add(permission); 358 } 359 360 ONE_TIME_PERMISSION_GROUPS = new ArraySet<>(); 361 ONE_TIME_PERMISSION_GROUPS.add(LOCATION); 362 ONE_TIME_PERMISSION_GROUPS.add(CAMERA); 363 ONE_TIME_PERMISSION_GROUPS.add(MICROPHONE); 364 365 PERM_GROUP_REQUEST_RES = new ArrayMap<>(); PERM_GROUP_REQUEST_RES.put(CONTACTS, R.string.permgrouprequest_contacts)366 PERM_GROUP_REQUEST_RES.put(CONTACTS, R.string.permgrouprequest_contacts); PERM_GROUP_REQUEST_RES.put(LOCATION, R.string.permgrouprequest_location)367 PERM_GROUP_REQUEST_RES.put(LOCATION, R.string.permgrouprequest_location); PERM_GROUP_REQUEST_RES.put(NEARBY_DEVICES, R.string.permgrouprequest_nearby_devices)368 PERM_GROUP_REQUEST_RES.put(NEARBY_DEVICES, R.string.permgrouprequest_nearby_devices); PERM_GROUP_REQUEST_RES.put(CALENDAR, R.string.permgrouprequest_calendar)369 PERM_GROUP_REQUEST_RES.put(CALENDAR, R.string.permgrouprequest_calendar); PERM_GROUP_REQUEST_RES.put(SMS, R.string.permgrouprequest_sms)370 PERM_GROUP_REQUEST_RES.put(SMS, R.string.permgrouprequest_sms); PERM_GROUP_REQUEST_RES.put(STORAGE, R.string.permgrouprequest_storage)371 PERM_GROUP_REQUEST_RES.put(STORAGE, R.string.permgrouprequest_storage); PERM_GROUP_REQUEST_RES.put(READ_MEDIA_AURAL, R.string.permgrouprequest_read_media_aural)372 PERM_GROUP_REQUEST_RES.put(READ_MEDIA_AURAL, R.string.permgrouprequest_read_media_aural); PERM_GROUP_REQUEST_RES.put(READ_MEDIA_VISUAL, R.string.permgrouprequest_read_media_visual)373 PERM_GROUP_REQUEST_RES.put(READ_MEDIA_VISUAL, R.string.permgrouprequest_read_media_visual); PERM_GROUP_REQUEST_RES.put(MICROPHONE, R.string.permgrouprequest_microphone)374 PERM_GROUP_REQUEST_RES.put(MICROPHONE, R.string.permgrouprequest_microphone); 375 PERM_GROUP_REQUEST_RES put(ACTIVITY_RECOGNITION, R.string.permgrouprequest_activityRecognition)376 .put(ACTIVITY_RECOGNITION, R.string.permgrouprequest_activityRecognition); PERM_GROUP_REQUEST_RES.put(CAMERA, R.string.permgrouprequest_camera)377 PERM_GROUP_REQUEST_RES.put(CAMERA, R.string.permgrouprequest_camera); PERM_GROUP_REQUEST_RES.put(CALL_LOG, R.string.permgrouprequest_calllog)378 PERM_GROUP_REQUEST_RES.put(CALL_LOG, R.string.permgrouprequest_calllog); PERM_GROUP_REQUEST_RES.put(PHONE, R.string.permgrouprequest_phone)379 PERM_GROUP_REQUEST_RES.put(PHONE, R.string.permgrouprequest_phone); PERM_GROUP_REQUEST_RES.put(SENSORS, R.string.permgrouprequest_sensors)380 PERM_GROUP_REQUEST_RES.put(SENSORS, R.string.permgrouprequest_sensors); PERM_GROUP_REQUEST_RES.put(NOTIFICATIONS, R.string.permgrouprequest_notifications)381 PERM_GROUP_REQUEST_RES.put(NOTIFICATIONS, R.string.permgrouprequest_notifications); 382 383 PERM_GROUP_REQUEST_DETAIL_RES = new ArrayMap<>(); PERM_GROUP_REQUEST_DETAIL_RES.put(LOCATION, R.string.permgrouprequestdetail_location)384 PERM_GROUP_REQUEST_DETAIL_RES.put(LOCATION, R.string.permgrouprequestdetail_location); PERM_GROUP_REQUEST_DETAIL_RES.put(MICROPHONE, R.string.permgrouprequestdetail_microphone)385 PERM_GROUP_REQUEST_DETAIL_RES.put(MICROPHONE, R.string.permgrouprequestdetail_microphone); PERM_GROUP_REQUEST_DETAIL_RES.put(CAMERA, R.string.permgrouprequestdetail_camera)386 PERM_GROUP_REQUEST_DETAIL_RES.put(CAMERA, R.string.permgrouprequestdetail_camera); 387 388 PERM_GROUP_BACKGROUND_REQUEST_RES = new ArrayMap<>(); 389 PERM_GROUP_BACKGROUND_REQUEST_RES put(LOCATION, R.string.permgroupbackgroundrequest_location)390 .put(LOCATION, R.string.permgroupbackgroundrequest_location); 391 PERM_GROUP_BACKGROUND_REQUEST_RES put(MICROPHONE, R.string.permgroupbackgroundrequest_microphone)392 .put(MICROPHONE, R.string.permgroupbackgroundrequest_microphone); 393 PERM_GROUP_BACKGROUND_REQUEST_RES put(CAMERA, R.string.permgroupbackgroundrequest_camera)394 .put(CAMERA, R.string.permgroupbackgroundrequest_camera); 395 PERM_GROUP_BACKGROUND_REQUEST_RES put(SENSORS, R.string.permgroupbackgroundrequest_sensors)396 .put(SENSORS, R.string.permgroupbackgroundrequest_sensors); 397 398 PERM_GROUP_BACKGROUND_REQUEST_DETAIL_RES = new ArrayMap<>(); 399 PERM_GROUP_BACKGROUND_REQUEST_DETAIL_RES put(LOCATION, R.string.permgroupbackgroundrequestdetail_location)400 .put(LOCATION, R.string.permgroupbackgroundrequestdetail_location); 401 PERM_GROUP_BACKGROUND_REQUEST_DETAIL_RES put(MICROPHONE, R.string.permgroupbackgroundrequestdetail_microphone)402 .put(MICROPHONE, R.string.permgroupbackgroundrequestdetail_microphone); 403 PERM_GROUP_BACKGROUND_REQUEST_DETAIL_RES put(CAMERA, R.string.permgroupbackgroundrequestdetail_camera)404 .put(CAMERA, R.string.permgroupbackgroundrequestdetail_camera); 405 PERM_GROUP_BACKGROUND_REQUEST_DETAIL_RES put(SENSORS, R.string.permgroupbackgroundrequestdetail_sensors)406 .put(SENSORS, R.string.permgroupbackgroundrequestdetail_sensors); 407 408 PERM_GROUP_UPGRADE_REQUEST_RES = new ArrayMap<>(); PERM_GROUP_UPGRADE_REQUEST_RES.put(LOCATION, R.string.permgroupupgraderequest_location)409 PERM_GROUP_UPGRADE_REQUEST_RES.put(LOCATION, R.string.permgroupupgraderequest_location); PERM_GROUP_UPGRADE_REQUEST_RES.put(MICROPHONE, R.string.permgroupupgraderequest_microphone)410 PERM_GROUP_UPGRADE_REQUEST_RES.put(MICROPHONE, R.string.permgroupupgraderequest_microphone); PERM_GROUP_UPGRADE_REQUEST_RES.put(CAMERA, R.string.permgroupupgraderequest_camera)411 PERM_GROUP_UPGRADE_REQUEST_RES.put(CAMERA, R.string.permgroupupgraderequest_camera); PERM_GROUP_UPGRADE_REQUEST_RES.put(SENSORS, R.string.permgroupupgraderequest_sensors)412 PERM_GROUP_UPGRADE_REQUEST_RES.put(SENSORS, R.string.permgroupupgraderequest_sensors); 413 414 PERM_GROUP_UPGRADE_REQUEST_DETAIL_RES = new ArrayMap<>(); 415 PERM_GROUP_UPGRADE_REQUEST_DETAIL_RES put(LOCATION, R.string.permgroupupgraderequestdetail_location)416 .put(LOCATION, R.string.permgroupupgraderequestdetail_location); 417 PERM_GROUP_UPGRADE_REQUEST_DETAIL_RES put(MICROPHONE, R.string.permgroupupgraderequestdetail_microphone)418 .put(MICROPHONE, R.string.permgroupupgraderequestdetail_microphone); 419 PERM_GROUP_UPGRADE_REQUEST_DETAIL_RES put(CAMERA, R.string.permgroupupgraderequestdetail_camera)420 .put(CAMERA, R.string.permgroupupgraderequestdetail_camera); 421 PERM_GROUP_UPGRADE_REQUEST_DETAIL_RES put(SENSORS, R.string.permgroupupgraderequestdetail_sensors)422 .put(SENSORS, R.string.permgroupupgraderequestdetail_sensors); 423 424 PERM_SENSOR_CODES = new ArrayMap<>(); 425 if (SdkLevel.isAtLeastS()) { PERM_SENSOR_CODES.put(CAMERA, SensorPrivacyManager.Sensors.CAMERA)426 PERM_SENSOR_CODES.put(CAMERA, SensorPrivacyManager.Sensors.CAMERA); PERM_SENSOR_CODES.put(MICROPHONE, SensorPrivacyManager.Sensors.MICROPHONE)427 PERM_SENSOR_CODES.put(MICROPHONE, SensorPrivacyManager.Sensors.MICROPHONE); 428 } 429 430 PERM_BLOCKED_ICON = new ArrayMap<>(); PERM_BLOCKED_ICON.put(CAMERA, R.drawable.ic_camera_blocked)431 PERM_BLOCKED_ICON.put(CAMERA, R.drawable.ic_camera_blocked); PERM_BLOCKED_ICON.put(MICROPHONE, R.drawable.ic_mic_blocked)432 PERM_BLOCKED_ICON.put(MICROPHONE, R.drawable.ic_mic_blocked); PERM_BLOCKED_ICON.put(LOCATION, R.drawable.ic_location_blocked)433 PERM_BLOCKED_ICON.put(LOCATION, R.drawable.ic_location_blocked); 434 435 PERM_BLOCKED_TITLE = new ArrayMap<>(); PERM_BLOCKED_TITLE.put(CAMERA, R.string.blocked_camera_title)436 PERM_BLOCKED_TITLE.put(CAMERA, R.string.blocked_camera_title); PERM_BLOCKED_TITLE.put(MICROPHONE, R.string.blocked_microphone_title)437 PERM_BLOCKED_TITLE.put(MICROPHONE, R.string.blocked_microphone_title); PERM_BLOCKED_TITLE.put(LOCATION, R.string.blocked_location_title)438 PERM_BLOCKED_TITLE.put(LOCATION, R.string.blocked_location_title); 439 440 } 441 Utils()442 private Utils() { 443 /* do nothing - hide constructor */ 444 } 445 446 private static ArrayMap<UserHandle, Context> sUserContexts = new ArrayMap<>(); 447 448 /** 449 * Creates and caches a PackageContext for the requested user, or returns the previously cached 450 * value. The package of the PackageContext is the application's package. 451 * 452 * @param app The currently running application 453 * @param user The desired user for the context 454 * 455 * @return The generated or cached Context for the requested user 456 * 457 * @throws PackageManager.NameNotFoundException If the app has no package name attached 458 */ getUserContext(Application app, UserHandle user)459 public static @NonNull Context getUserContext(Application app, UserHandle user) throws 460 PackageManager.NameNotFoundException { 461 if (!sUserContexts.containsKey(user)) { 462 sUserContexts.put(user, app.getApplicationContext() 463 .createPackageContextAsUser(app.getPackageName(), 0, user)); 464 } 465 return sUserContexts.get(user); 466 } 467 468 /** 469 * {@code @NonNull} version of {@link Context#getSystemService(Class)} 470 */ getSystemServiceSafe(@onNull Context context, Class<M> clazz)471 public static @NonNull <M> M getSystemServiceSafe(@NonNull Context context, Class<M> clazz) { 472 return Preconditions.checkNotNull(context.getSystemService(clazz), 473 "Could not resolve " + clazz.getSimpleName()); 474 } 475 476 /** 477 * {@code @NonNull} version of {@link Context#getSystemService(Class)} 478 */ getSystemServiceSafe(@onNull Context context, Class<M> clazz, @NonNull UserHandle user)479 public static @NonNull <M> M getSystemServiceSafe(@NonNull Context context, Class<M> clazz, 480 @NonNull UserHandle user) { 481 try { 482 return Preconditions.checkNotNull(context.createPackageContextAsUser( 483 context.getPackageName(), 0, user).getSystemService(clazz), 484 "Could not resolve " + clazz.getSimpleName()); 485 } catch (PackageManager.NameNotFoundException neverHappens) { 486 throw new IllegalStateException(); 487 } 488 } 489 490 /** 491 * {@code @NonNull} version of {@link Intent#getParcelableExtra(String)} 492 */ getParcelableExtraSafe(@onNull Intent intent, @NonNull String name)493 public static @NonNull <T extends Parcelable> T getParcelableExtraSafe(@NonNull Intent intent, 494 @NonNull String name) { 495 return Preconditions.checkNotNull(intent.getParcelableExtra(name), 496 "Could not get parcelable extra for " + name); 497 } 498 499 /** 500 * {@code @NonNull} version of {@link Intent#getStringExtra(String)} 501 */ getStringExtraSafe(@onNull Intent intent, @NonNull String name)502 public static @NonNull String getStringExtraSafe(@NonNull Intent intent, 503 @NonNull String name) { 504 return Preconditions.checkNotNull(intent.getStringExtra(name), 505 "Could not get string extra for " + name); 506 } 507 508 /** 509 * Returns true if a permission is dangerous, installed, and not removed 510 * @param permissionInfo The permission we wish to check 511 * @return If all of the conditions are met 512 */ isPermissionDangerousInstalledNotRemoved(PermissionInfo permissionInfo)513 public static boolean isPermissionDangerousInstalledNotRemoved(PermissionInfo permissionInfo) { 514 return permissionInfo != null 515 && permissionInfo.getProtection() == PermissionInfo.PROTECTION_DANGEROUS 516 && (permissionInfo.flags & PermissionInfo.FLAG_INSTALLED) != 0 517 && (permissionInfo.flags & PermissionInfo.FLAG_REMOVED) == 0; 518 } 519 520 /** 521 * Get permission group a platform permission belongs to, or null if the permission is not a 522 * platform permission. 523 * 524 * @param permission the permission to resolve 525 * 526 * @return The group the permission belongs to 527 */ getGroupOfPlatformPermission(@onNull String permission)528 public static @Nullable String getGroupOfPlatformPermission(@NonNull String permission) { 529 return PLATFORM_PERMISSIONS.get(permission); 530 } 531 532 /** 533 * Get name of the permission group a permission belongs to. 534 * 535 * @param permission the {@link PermissionInfo info} of the permission to resolve 536 * 537 * @return The group the permission belongs to 538 */ getGroupOfPermission(@onNull PermissionInfo permission)539 public static @Nullable String getGroupOfPermission(@NonNull PermissionInfo permission) { 540 String groupName = Utils.getGroupOfPlatformPermission(permission.name); 541 if (groupName == null) { 542 groupName = permission.group; 543 } 544 545 return groupName; 546 } 547 548 /** 549 * Get the names for all platform permissions belonging to a group. 550 * 551 * @param group the group 552 * 553 * @return The permission names or an empty list if the 554 * group is not does not have platform runtime permissions 555 */ getPlatformPermissionNamesOfGroup(@onNull String group)556 public static @NonNull List<String> getPlatformPermissionNamesOfGroup(@NonNull String group) { 557 final ArrayList<String> permissions = PLATFORM_PERMISSION_GROUPS.get(group); 558 return (permissions != null) ? permissions : Collections.emptyList(); 559 } 560 561 /** 562 * Get the {@link PermissionInfo infos} for all platform permissions belonging to a group. 563 * 564 * @param pm Package manager to use to resolve permission infos 565 * @param group the group 566 * 567 * @return The infos for platform permissions belonging to the group or an empty list if the 568 * group is not does not have platform runtime permissions 569 */ getPlatformPermissionsOfGroup( @onNull PackageManager pm, @NonNull String group)570 public static @NonNull List<PermissionInfo> getPlatformPermissionsOfGroup( 571 @NonNull PackageManager pm, @NonNull String group) { 572 ArrayList<PermissionInfo> permInfos = new ArrayList<>(); 573 574 ArrayList<String> permissions = PLATFORM_PERMISSION_GROUPS.get(group); 575 if (permissions == null) { 576 return Collections.emptyList(); 577 } 578 579 int numPermissions = permissions.size(); 580 for (int i = 0; i < numPermissions; i++) { 581 String permName = permissions.get(i); 582 PermissionInfo permInfo; 583 try { 584 permInfo = pm.getPermissionInfo(permName, 0); 585 } catch (PackageManager.NameNotFoundException e) { 586 throw new IllegalStateException(permName + " not defined by platform", e); 587 } 588 589 permInfos.add(permInfo); 590 } 591 592 return permInfos; 593 } 594 595 /** 596 * Get the {@link PermissionInfo infos} for all permission infos belonging to a group. 597 * 598 * @param pm Package manager to use to resolve permission infos 599 * @param group the group 600 * 601 * @return The infos of permissions belonging to the group or an empty list if the group 602 * does not have runtime permissions 603 */ getPermissionInfosForGroup( @onNull PackageManager pm, @NonNull String group)604 public static @NonNull List<PermissionInfo> getPermissionInfosForGroup( 605 @NonNull PackageManager pm, @NonNull String group) 606 throws PackageManager.NameNotFoundException { 607 List<PermissionInfo> permissions = pm.queryPermissionsByGroup(group, 0); 608 permissions.addAll(getPlatformPermissionsOfGroup(pm, group)); 609 610 /* 611 * If the undefined group is requested, the package manager will return all platform 612 * permissions, since they are marked as Undefined in the manifest. Do not return these 613 * permissions. 614 */ 615 if (group.equals(Manifest.permission_group.UNDEFINED)) { 616 List<PermissionInfo> undefinedPerms = new ArrayList<>(); 617 for (PermissionInfo permissionInfo : permissions) { 618 String permGroup = getGroupOfPlatformPermission(permissionInfo.name); 619 if (permGroup == null || permGroup.equals(Manifest.permission_group.UNDEFINED)) { 620 undefinedPerms.add(permissionInfo); 621 } 622 } 623 return undefinedPerms; 624 } 625 626 return permissions; 627 } 628 629 /** 630 * Get the {@link PermissionInfo infos} for all runtime installed permission infos belonging to 631 * a group. 632 * 633 * @param pm Package manager to use to resolve permission infos 634 * @param group the group 635 * 636 * @return The infos of installed runtime permissions belonging to the group or an empty list 637 * if the group does not have runtime permissions 638 */ getInstalledRuntimePermissionInfosForGroup( @onNull PackageManager pm, @NonNull String group)639 public static @NonNull List<PermissionInfo> getInstalledRuntimePermissionInfosForGroup( 640 @NonNull PackageManager pm, @NonNull String group) 641 throws PackageManager.NameNotFoundException { 642 List<PermissionInfo> permissions = pm.queryPermissionsByGroup(group, 0); 643 permissions.addAll(getPlatformPermissionsOfGroup(pm, group)); 644 645 List<PermissionInfo> installedRuntime = new ArrayList<>(); 646 for (PermissionInfo permissionInfo: permissions) { 647 if (permissionInfo.getProtection() == PermissionInfo.PROTECTION_DANGEROUS 648 && (permissionInfo.flags & PermissionInfo.FLAG_INSTALLED) != 0 649 && (permissionInfo.flags & PermissionInfo.FLAG_REMOVED) == 0) { 650 installedRuntime.add(permissionInfo); 651 } 652 } 653 654 /* 655 * If the undefined group is requested, the package manager will return all platform 656 * permissions, since they are marked as Undefined in the manifest. Do not return these 657 * permissions. 658 */ 659 if (group.equals(Manifest.permission_group.UNDEFINED)) { 660 List<PermissionInfo> undefinedPerms = new ArrayList<>(); 661 for (PermissionInfo permissionInfo : installedRuntime) { 662 String permGroup = getGroupOfPlatformPermission(permissionInfo.name); 663 if (permGroup == null || permGroup.equals(Manifest.permission_group.UNDEFINED)) { 664 undefinedPerms.add(permissionInfo); 665 } 666 } 667 return undefinedPerms; 668 } 669 670 return installedRuntime; 671 } 672 673 /** 674 * Get the {@link PackageItemInfo infos} for the given permission group. 675 * 676 * @param groupName the group 677 * @param context the {@code Context} to retrieve {@code PackageManager} 678 * 679 * @return The info of permission group or null if the group does not have runtime permissions. 680 */ getGroupInfo(@onNull String groupName, @NonNull Context context)681 public static @Nullable PackageItemInfo getGroupInfo(@NonNull String groupName, 682 @NonNull Context context) { 683 try { 684 return context.getPackageManager().getPermissionGroupInfo(groupName, 0); 685 } catch (NameNotFoundException e) { 686 /* ignore */ 687 } 688 try { 689 return context.getPackageManager().getPermissionInfo(groupName, 0); 690 } catch (NameNotFoundException e) { 691 /* ignore */ 692 } 693 return null; 694 } 695 696 /** 697 * Get the {@link PermissionInfo infos} for all permission infos belonging to a group. 698 * 699 * @param groupName the group 700 * @param context the {@code Context} to retrieve {@code PackageManager} 701 * 702 * @return The infos of permissions belonging to the group or null if the group does not have 703 * runtime permissions. 704 */ getGroupPermissionInfos(@onNull String groupName, @NonNull Context context)705 public static @Nullable List<PermissionInfo> getGroupPermissionInfos(@NonNull String groupName, 706 @NonNull Context context) { 707 try { 708 return Utils.getPermissionInfosForGroup(context.getPackageManager(), groupName); 709 } catch (NameNotFoundException e) { 710 /* ignore */ 711 } 712 try { 713 PermissionInfo permissionInfo = context.getPackageManager() 714 .getPermissionInfo(groupName, 0); 715 List<PermissionInfo> permissions = new ArrayList<>(); 716 permissions.add(permissionInfo); 717 return permissions; 718 } catch (NameNotFoundException e) { 719 /* ignore */ 720 } 721 return null; 722 } 723 724 /** 725 * Get the label for an application, truncating if it is too long. 726 * 727 * @param applicationInfo the {@link ApplicationInfo} of the application 728 * @param context the {@code Context} to retrieve {@code PackageManager} 729 * 730 * @return the label for the application 731 */ 732 @NonNull getAppLabel(@onNull ApplicationInfo applicationInfo, @NonNull Context context)733 public static String getAppLabel(@NonNull ApplicationInfo applicationInfo, 734 @NonNull Context context) { 735 return getAppLabel(applicationInfo, DEFAULT_MAX_LABEL_SIZE_PX, context); 736 } 737 738 /** 739 * Get the full label for an application without truncation. 740 * 741 * @param applicationInfo the {@link ApplicationInfo} of the application 742 * @param context the {@code Context} to retrieve {@code PackageManager} 743 * 744 * @return the label for the application 745 */ 746 @NonNull getFullAppLabel(@onNull ApplicationInfo applicationInfo, @NonNull Context context)747 public static String getFullAppLabel(@NonNull ApplicationInfo applicationInfo, 748 @NonNull Context context) { 749 return getAppLabel(applicationInfo, 0, context); 750 } 751 752 /** 753 * Get the label for an application with the ability to control truncating. 754 * 755 * @param applicationInfo the {@link ApplicationInfo} of the application 756 * @param ellipsizeDip see {@link TextUtils#makeSafeForPresentation}. 757 * @param context the {@code Context} to retrieve {@code PackageManager} 758 * 759 * @return the label for the application 760 */ 761 @NonNull getAppLabel(@onNull ApplicationInfo applicationInfo, float ellipsizeDip, @NonNull Context context)762 private static String getAppLabel(@NonNull ApplicationInfo applicationInfo, float ellipsizeDip, 763 @NonNull Context context) { 764 return BidiFormatter.getInstance().unicodeWrap(applicationInfo.loadSafeLabel( 765 context.getPackageManager(), ellipsizeDip, 766 TextUtils.SAFE_STRING_FLAG_TRIM | TextUtils.SAFE_STRING_FLAG_FIRST_LINE) 767 .toString()); 768 } 769 loadDrawable(PackageManager pm, String pkg, int resId)770 public static Drawable loadDrawable(PackageManager pm, String pkg, int resId) { 771 try { 772 return pm.getResourcesForApplication(pkg).getDrawable(resId, null); 773 } catch (Resources.NotFoundException | PackageManager.NameNotFoundException e) { 774 Log.d(LOG_TAG, "Couldn't get resource", e); 775 return null; 776 } 777 } 778 isModernPermissionGroup(String name)779 public static boolean isModernPermissionGroup(String name) { 780 return PLATFORM_PERMISSION_GROUPS.containsKey(name); 781 } 782 783 /** 784 * Get the names of the platform permission groups. 785 * 786 * @return the names of the platform permission groups. 787 */ getPlatformPermissionGroups()788 public static List<String> getPlatformPermissionGroups() { 789 return new ArrayList<>(PLATFORM_PERMISSION_GROUPS.keySet()); 790 } 791 792 /** 793 * Get the names of the runtime platform permissions 794 * 795 * @return the names of the runtime platform permissions. 796 */ getRuntimePlatformPermissionNames()797 public static List<String> getRuntimePlatformPermissionNames() { 798 return new ArrayList<>(PLATFORM_PERMISSIONS.keySet()); 799 } 800 801 /** 802 * Is the permissions a platform runtime permission 803 * 804 * @return the names of the runtime platform permissions. 805 */ isRuntimePlatformPermission(@onNull String permission)806 public static boolean isRuntimePlatformPermission(@NonNull String permission) { 807 return PLATFORM_PERMISSIONS.containsKey(permission); 808 } 809 810 /** 811 * Should UI show this permission. 812 * 813 * <p>If the user cannot change the group, it should not be shown. 814 * 815 * @param group The group that might need to be shown to the user 816 * 817 * @return 818 */ shouldShowPermission(Context context, AppPermissionGroup group)819 public static boolean shouldShowPermission(Context context, AppPermissionGroup group) { 820 if (!group.isGrantingAllowed()) { 821 return false; 822 } 823 824 final boolean isPlatformPermission = group.getDeclaringPackage().equals(OS_PKG); 825 // Show legacy permissions only if the user chose that. 826 if (isPlatformPermission 827 && !Utils.isModernPermissionGroup(group.getName())) { 828 return false; 829 } 830 return true; 831 } 832 applyTint(Context context, Drawable icon, int attr)833 public static Drawable applyTint(Context context, Drawable icon, int attr) { 834 Theme theme = context.getTheme(); 835 TypedValue typedValue = new TypedValue(); 836 theme.resolveAttribute(attr, typedValue, true); 837 icon = icon.mutate(); 838 icon.setTint(context.getColor(typedValue.resourceId)); 839 return icon; 840 } 841 applyTint(Context context, int iconResId, int attr)842 public static Drawable applyTint(Context context, int iconResId, int attr) { 843 return applyTint(context, context.getDrawable(iconResId), attr); 844 } 845 getAllInstalledApplications(Context context)846 public static List<ApplicationInfo> getAllInstalledApplications(Context context) { 847 return context.getPackageManager().getInstalledApplications(0); 848 } 849 850 /** 851 * Is the group or background group user sensitive? 852 * 853 * @param group The group that might be user sensitive 854 * 855 * @return {@code true} if the group (or it's subgroup) is user sensitive. 856 */ isGroupOrBgGroupUserSensitive(AppPermissionGroup group)857 public static boolean isGroupOrBgGroupUserSensitive(AppPermissionGroup group) { 858 return group.isUserSensitive() || (group.getBackgroundPermissions() != null 859 && group.getBackgroundPermissions().isUserSensitive()); 860 } 861 areGroupPermissionsIndividuallyControlled(Context context, String group)862 public static boolean areGroupPermissionsIndividuallyControlled(Context context, String group) { 863 if (!context.getPackageManager().arePermissionsIndividuallyControlled()) { 864 return false; 865 } 866 return Manifest.permission_group.SMS.equals(group) 867 || Manifest.permission_group.PHONE.equals(group) 868 || Manifest.permission_group.CONTACTS.equals(group) 869 || Manifest.permission_group.CALL_LOG.equals(group); 870 } 871 isPermissionIndividuallyControlled(Context context, String permission)872 public static boolean isPermissionIndividuallyControlled(Context context, String permission) { 873 if (!context.getPackageManager().arePermissionsIndividuallyControlled()) { 874 return false; 875 } 876 return Manifest.permission.READ_CONTACTS.equals(permission) 877 || Manifest.permission.WRITE_CONTACTS.equals(permission) 878 || Manifest.permission.SEND_SMS.equals(permission) 879 || Manifest.permission.RECEIVE_SMS.equals(permission) 880 || Manifest.permission.READ_SMS.equals(permission) 881 || Manifest.permission.RECEIVE_MMS.equals(permission) 882 || Manifest.permission.CALL_PHONE.equals(permission) 883 || Manifest.permission.READ_CALL_LOG.equals(permission) 884 || Manifest.permission.WRITE_CALL_LOG.equals(permission); 885 } 886 887 /** 888 * Get the message shown to grant a permission group to an app. 889 * 890 * @param appLabel The label of the app 891 * @param packageName The package name of the app 892 * @param groupName The name of the permission group 893 * @param context A context to resolve resources 894 * @param requestRes The resource id of the grant request message 895 * 896 * @return The formatted message to be used as title when granting permissions 897 */ getRequestMessage(CharSequence appLabel, String packageName, String groupName, Context context, @StringRes int requestRes)898 public static CharSequence getRequestMessage(CharSequence appLabel, String packageName, 899 String groupName, Context context, @StringRes int requestRes) { 900 901 boolean isIsolatedStorage; 902 try { 903 isIsolatedStorage = !isNonIsolatedStorage(context, packageName); 904 } catch (NameNotFoundException e) { 905 isIsolatedStorage = false; 906 } 907 if (groupName.equals(STORAGE) && isIsolatedStorage) { 908 return Html.fromHtml( 909 String.format(context.getResources().getConfiguration().getLocales().get(0), 910 context.getString(R.string.permgrouprequest_storage_isolated), 911 appLabel), 0); 912 } else if (requestRes != 0) { 913 return Html.fromHtml(context.getResources().getString(requestRes, appLabel), 0); 914 } 915 916 return Html.fromHtml(context.getString(R.string.permission_warning_template, appLabel, 917 loadGroupDescription(context, groupName, context.getPackageManager())), 0); 918 } 919 loadGroupDescription(Context context, String groupName, @NonNull PackageManager packageManager)920 private static CharSequence loadGroupDescription(Context context, String groupName, 921 @NonNull PackageManager packageManager) { 922 PackageItemInfo groupInfo = getGroupInfo(groupName, context); 923 CharSequence description = null; 924 if (groupInfo instanceof PermissionGroupInfo) { 925 description = ((PermissionGroupInfo) groupInfo).loadDescription(packageManager); 926 } else if (groupInfo instanceof PermissionInfo) { 927 description = ((PermissionInfo) groupInfo).loadDescription(packageManager); 928 } 929 930 if (description == null || description.length() <= 0) { 931 description = context.getString(R.string.default_permission_description); 932 } 933 934 return description; 935 } 936 937 /** 938 * Whether or not the given package has non-isolated storage permissions 939 * @param context The current context 940 * @param packageName The package name to check 941 * @return True if the package has access to non-isolated storage, false otherwise 942 * @throws NameNotFoundException 943 */ isNonIsolatedStorage(@onNull Context context, @NonNull String packageName)944 public static boolean isNonIsolatedStorage(@NonNull Context context, 945 @NonNull String packageName) throws NameNotFoundException { 946 PackageInfo packageInfo = context.getPackageManager().getPackageInfo(packageName, 0); 947 AppOpsManager manager = context.getSystemService(AppOpsManager.class); 948 949 950 return packageInfo.applicationInfo.targetSdkVersion < Build.VERSION_CODES.P 951 || (packageInfo.applicationInfo.targetSdkVersion < Build.VERSION_CODES.R 952 && manager.unsafeCheckOpNoThrow(OPSTR_LEGACY_STORAGE, 953 packageInfo.applicationInfo.uid, packageInfo.packageName) == MODE_ALLOWED); 954 } 955 956 /** 957 * Gets whether the STORAGE group should be hidden from the UI for this package. This is true 958 * when the platform is T+, and the package has legacy storage access (i.e., either the package 959 * has a targetSdk less than Q, or has a targetSdk equal to Q and has OPSTR_LEGACY_STORAGE). 960 * 961 * TODO jaysullivan: This is always calling AppOpsManager; not taking advantage of LiveData 962 * 963 * @param pkg The package to check 964 */ shouldShowStorage(LightPackageInfo pkg)965 public static boolean shouldShowStorage(LightPackageInfo pkg) { 966 if (!SdkLevel.isAtLeastT()) { 967 return true; 968 } 969 int targetSdkVersion = pkg.getTargetSdkVersion(); 970 PermissionControllerApplication app = PermissionControllerApplication.get(); 971 Context context = null; 972 try { 973 context = Utils.getUserContext(app, UserHandle.getUserHandleForUid(pkg.getUid())); 974 } catch (NameNotFoundException e) { 975 return true; 976 } 977 AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class); 978 if (appOpsManager == null) { 979 return true; 980 } 981 982 return targetSdkVersion < Build.VERSION_CODES.Q 983 || (targetSdkVersion == Build.VERSION_CODES.Q 984 && appOpsManager.unsafeCheckOpNoThrow(OPSTR_LEGACY_STORAGE, pkg.getUid(), 985 pkg.getPackageName()) == MODE_ALLOWED); 986 } 987 988 /** 989 * Build a string representing the given time if it happened on the current day and the date 990 * otherwise. 991 * 992 * @param context the context. 993 * @param lastAccessTime the time in milliseconds. 994 * 995 * @return a string representing the time or date of the given time or null if the time is 0. 996 */ getAbsoluteTimeString(@onNull Context context, long lastAccessTime)997 public static @Nullable String getAbsoluteTimeString(@NonNull Context context, 998 long lastAccessTime) { 999 if (lastAccessTime == 0) { 1000 return null; 1001 } 1002 if (isToday(lastAccessTime)) { 1003 return DateFormat.getTimeFormat(context).format(lastAccessTime); 1004 } else { 1005 return DateFormat.getMediumDateFormat(context).format(lastAccessTime); 1006 } 1007 } 1008 1009 /** 1010 * Check whether the given time (in milliseconds) is in the current day. 1011 * 1012 * @param time the time in milliseconds 1013 * 1014 * @return whether the given time is in the current day. 1015 */ isToday(long time)1016 private static boolean isToday(long time) { 1017 Calendar today = Calendar.getInstance(Locale.getDefault()); 1018 today.setTimeInMillis(System.currentTimeMillis()); 1019 today.set(Calendar.HOUR_OF_DAY, 0); 1020 today.set(Calendar.MINUTE, 0); 1021 today.set(Calendar.SECOND, 0); 1022 today.set(Calendar.MILLISECOND, 0); 1023 1024 Calendar date = Calendar.getInstance(Locale.getDefault()); 1025 date.setTimeInMillis(time); 1026 return !date.before(today); 1027 } 1028 1029 /** 1030 * Add a menu item for searching Settings, if there is an activity handling the action. 1031 * 1032 * @param menu the menu to add the menu item into 1033 * @param context the context for checking whether there is an activity handling the action 1034 */ prepareSearchMenuItem(@onNull Menu menu, @NonNull Context context)1035 public static void prepareSearchMenuItem(@NonNull Menu menu, @NonNull Context context) { 1036 Intent intent = new Intent(Settings.ACTION_APP_SEARCH_SETTINGS); 1037 if (context.getPackageManager().resolveActivity(intent, 0) == null) { 1038 return; 1039 } 1040 MenuItem searchItem = menu.add(Menu.NONE, Menu.NONE, Menu.NONE, R.string.search_menu); 1041 searchItem.setIcon(R.drawable.ic_search_24dp); 1042 searchItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); 1043 searchItem.setOnMenuItemClickListener(item -> { 1044 try { 1045 context.startActivity(intent); 1046 } catch (ActivityNotFoundException e) { 1047 Log.e(LOG_TAG, "Cannot start activity to search settings", e); 1048 } 1049 return true; 1050 }); 1051 } 1052 1053 /** 1054 * Get badged app icon if necessary, similar as used in the Settings UI. 1055 * 1056 * @param context The context to use 1057 * @param appInfo The app the icon belong to 1058 * 1059 * @return The icon to use 1060 */ getBadgedIcon(@onNull Context context, @NonNull ApplicationInfo appInfo)1061 public static @NonNull Drawable getBadgedIcon(@NonNull Context context, 1062 @NonNull ApplicationInfo appInfo) { 1063 UserHandle user = UserHandle.getUserHandleForUid(appInfo.uid); 1064 try (IconFactory iconFactory = IconFactory.obtain(context)) { 1065 return iconFactory.createBadgedIconBitmap( 1066 appInfo.loadUnbadgedIcon(context.getPackageManager()), 1067 new IconFactory.IconOptions() 1068 .setShrinkNonAdaptiveIcons(false) 1069 .setUser(user)) 1070 .newIcon(context); 1071 } 1072 } 1073 1074 /** 1075 * Get a string saying what apps with the given permission group can do. 1076 * 1077 * @param context The context to use 1078 * @param groupName The name of the permission group 1079 * @param description The description of the permission group 1080 * 1081 * @return a string saying what apps with the given permission group can do. 1082 */ getPermissionGroupDescriptionString(@onNull Context context, @NonNull String groupName, @NonNull CharSequence description)1083 public static @NonNull String getPermissionGroupDescriptionString(@NonNull Context context, 1084 @NonNull String groupName, @NonNull CharSequence description) { 1085 switch (groupName) { 1086 case ACTIVITY_RECOGNITION: 1087 return context.getString( 1088 R.string.permission_description_summary_activity_recognition); 1089 case CALENDAR: 1090 return context.getString(R.string.permission_description_summary_calendar); 1091 case CALL_LOG: 1092 return context.getString(R.string.permission_description_summary_call_log); 1093 case CAMERA: 1094 return context.getString(R.string.permission_description_summary_camera); 1095 case CONTACTS: 1096 return context.getString(R.string.permission_description_summary_contacts); 1097 case LOCATION: 1098 return context.getString(R.string.permission_description_summary_location); 1099 case MICROPHONE: 1100 return context.getString(R.string.permission_description_summary_microphone); 1101 case NEARBY_DEVICES: 1102 return context.getString(R.string.permission_description_summary_nearby_devices); 1103 case PHONE: 1104 return context.getString(R.string.permission_description_summary_phone); 1105 case READ_MEDIA_AURAL: 1106 return context.getString(R.string.permission_description_summary_read_media_aural); 1107 case READ_MEDIA_VISUAL: 1108 return context.getString(R.string.permission_description_summary_read_media_visual); 1109 case SENSORS: 1110 return context.getString(R.string.permission_description_summary_sensors); 1111 case SMS: 1112 return context.getString(R.string.permission_description_summary_sms); 1113 case STORAGE: 1114 return context.getString(R.string.permission_description_summary_storage); 1115 default: 1116 return context.getString(R.string.permission_description_summary_generic, 1117 description); 1118 } 1119 } 1120 1121 /** 1122 * Whether the Location Access Check is enabled. 1123 * 1124 * @return {@code true} iff the Location Access Check is enabled. 1125 */ isLocationAccessCheckEnabled()1126 public static boolean isLocationAccessCheckEnabled() { 1127 return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, 1128 PROPERTY_LOCATION_ACCESS_CHECK_ENABLED, true); 1129 } 1130 1131 /** 1132 * Get a device protected storage based shared preferences. Avoid storing sensitive data in it. 1133 * 1134 * @param context the context to get the shared preferences 1135 * @return a device protected storage based shared preferences 1136 */ 1137 @NonNull getDeviceProtectedSharedPreferences(@onNull Context context)1138 public static SharedPreferences getDeviceProtectedSharedPreferences(@NonNull Context context) { 1139 if (!context.isDeviceProtectedStorage()) { 1140 context = context.createDeviceProtectedStorageContext(); 1141 } 1142 return context.getSharedPreferences(Constants.PREFERENCES_FILE, MODE_PRIVATE); 1143 } 1144 getOneTimePermissionsTimeout()1145 public static long getOneTimePermissionsTimeout() { 1146 return DeviceConfig.getLong(DeviceConfig.NAMESPACE_PERMISSIONS, 1147 PROPERTY_ONE_TIME_PERMISSIONS_TIMEOUT_MILLIS, ONE_TIME_PERMISSIONS_TIMEOUT_MILLIS); 1148 } 1149 1150 /** 1151 * Returns the delay in milliseconds before revoking permissions at the end of a one-time 1152 * permission session if all processes have been killed. 1153 * If the session was triggered by a self-revocation, then revocation should happen 1154 * immediately. For a regular one-time permission session, a grace period allows a quick 1155 * app restart without losing the permission. 1156 * @param isSelfRevoked If true, return the delay for a self-revocation session. Otherwise, 1157 * return delay for a regular one-time permission session. 1158 */ getOneTimePermissionsKilledDelay(boolean isSelfRevoked)1159 public static long getOneTimePermissionsKilledDelay(boolean isSelfRevoked) { 1160 if (isSelfRevoked) { 1161 // For a self-revoked session, we revoke immediately when the process dies. 1162 return 0; 1163 } 1164 return DeviceConfig.getLong(DeviceConfig.NAMESPACE_PERMISSIONS, 1165 PROPERTY_ONE_TIME_PERMISSIONS_KILLED_DELAY_MILLIS, 1166 ONE_TIME_PERMISSIONS_KILLED_DELAY_MILLIS); 1167 } 1168 1169 /** 1170 * Get context of the parent user of the profile group (i.e. usually the 'personal' profile, 1171 * not the 'work' profile). 1172 * 1173 * @param context The context of a user of the profile user group. 1174 * 1175 * @return The context of the parent user 1176 */ getParentUserContext(@onNull Context context)1177 public static Context getParentUserContext(@NonNull Context context) { 1178 UserHandle parentUser = getSystemServiceSafe(context, UserManager.class) 1179 .getProfileParent(UserHandle.of(myUserId())); 1180 1181 if (parentUser == null) { 1182 return context; 1183 } 1184 1185 // In a multi profile environment perform all operations as the parent user of the 1186 // current profile 1187 try { 1188 return context.createPackageContextAsUser(context.getPackageName(), 0, 1189 parentUser); 1190 } catch (PackageManager.NameNotFoundException e) { 1191 // cannot happen 1192 throw new IllegalStateException("Could not switch to parent user " + parentUser, e); 1193 } 1194 } 1195 1196 /** 1197 * Whether the permission group supports one-time 1198 * @param permissionGroup The permission group to check 1199 * @return {@code true} iff the group supports one-time 1200 */ supportsOneTimeGrant(String permissionGroup)1201 public static boolean supportsOneTimeGrant(String permissionGroup) { 1202 return ONE_TIME_PERMISSION_GROUPS.contains(permissionGroup); 1203 } 1204 1205 /** 1206 * The resource id for the request message for a permission group 1207 * @param groupName Permission group name 1208 * @return The id or 0 if the permission group doesn't exist or have a message 1209 */ getRequest(String groupName)1210 public static int getRequest(String groupName) { 1211 return PERM_GROUP_REQUEST_RES.getOrDefault(groupName, 0); 1212 } 1213 1214 /** 1215 * The resource id for the request detail message for a permission group 1216 * @param groupName Permission group name 1217 * @return The id or 0 if the permission group doesn't exist or have a message 1218 */ getRequestDetail(String groupName)1219 public static int getRequestDetail(String groupName) { 1220 return PERM_GROUP_REQUEST_DETAIL_RES.getOrDefault(groupName, 0); 1221 } 1222 1223 /** 1224 * The resource id for the background request message for a permission group 1225 * @param groupName Permission group name 1226 * @return The id or 0 if the permission group doesn't exist or have a message 1227 */ getBackgroundRequest(String groupName)1228 public static int getBackgroundRequest(String groupName) { 1229 return PERM_GROUP_BACKGROUND_REQUEST_RES.getOrDefault(groupName, 0); 1230 } 1231 1232 /** 1233 * The resource id for the background request detail message for a permission group 1234 * @param groupName Permission group name 1235 * @return The id or 0 if the permission group doesn't exist or have a message 1236 */ getBackgroundRequestDetail(String groupName)1237 public static int getBackgroundRequestDetail(String groupName) { 1238 return PERM_GROUP_BACKGROUND_REQUEST_DETAIL_RES.getOrDefault(groupName, 0); 1239 } 1240 1241 /** 1242 * The resource id for the upgrade request message for a permission group 1243 * @param groupName Permission group name 1244 * @return The id or 0 if the permission group doesn't exist or have a message 1245 */ getUpgradeRequest(String groupName)1246 public static int getUpgradeRequest(String groupName) { 1247 return PERM_GROUP_UPGRADE_REQUEST_RES.getOrDefault(groupName, 0); 1248 } 1249 1250 /** 1251 * The resource id for the upgrade request detail message for a permission group 1252 * @param groupName Permission group name 1253 * @return The id or 0 if the permission group doesn't exist or have a message 1254 */ getUpgradeRequestDetail(String groupName)1255 public static int getUpgradeRequestDetail(String groupName) { 1256 return PERM_GROUP_UPGRADE_REQUEST_DETAIL_RES.getOrDefault(groupName, 0); 1257 } 1258 1259 /** 1260 * Checks whether a package has an active one-time permission according to the system server's 1261 * flags 1262 * 1263 * @param context the {@code Context} to retrieve {@code PackageManager} 1264 * @param packageName The package to check for 1265 * @return Whether a package has an active one-time permission 1266 */ hasOneTimePermissions(Context context, String packageName)1267 public static boolean hasOneTimePermissions(Context context, String packageName) { 1268 String[] permissions; 1269 PackageManager pm = context.getPackageManager(); 1270 try { 1271 permissions = pm.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS) 1272 .requestedPermissions; 1273 } catch (NameNotFoundException e) { 1274 Log.w(LOG_TAG, "Checking for one-time permissions in nonexistent package"); 1275 return false; 1276 } 1277 if (permissions == null) { 1278 return false; 1279 } 1280 for (String permissionName : permissions) { 1281 if ((pm.getPermissionFlags(permissionName, packageName, Process.myUserHandle()) 1282 & PackageManager.FLAG_PERMISSION_ONE_TIME) != 0 1283 && pm.checkPermission(permissionName, packageName) 1284 == PackageManager.PERMISSION_GRANTED) { 1285 return true; 1286 } 1287 } 1288 return false; 1289 } 1290 1291 /** 1292 * Returns a random session ID value that's guaranteed to not be {@code INVALID_SESSION_ID}. 1293 * 1294 * @return A valid session ID. 1295 */ getValidSessionId()1296 public static long getValidSessionId() { 1297 long sessionId = INVALID_SESSION_ID; 1298 while (sessionId == INVALID_SESSION_ID) { 1299 sessionId = new Random().nextLong(); 1300 } 1301 return sessionId; 1302 } 1303 1304 /** 1305 * Gets the label of the Settings application 1306 * 1307 * @param pm The packageManager used to get the activity resolution 1308 * 1309 * @return The CharSequence title of the settings app 1310 */ 1311 @Nullable getSettingsLabelForNotifications(PackageManager pm)1312 public static CharSequence getSettingsLabelForNotifications(PackageManager pm) { 1313 // We pretend we're the Settings app sending the notification, so figure out its name. 1314 Intent openSettingsIntent = new Intent(Settings.ACTION_SETTINGS); 1315 ResolveInfo resolveInfo = pm.resolveActivity(openSettingsIntent, MATCH_SYSTEM_ONLY); 1316 if (resolveInfo == null) { 1317 return null; 1318 } 1319 return pm.getApplicationLabel(resolveInfo.activityInfo.applicationInfo); 1320 } 1321 1322 /** 1323 * Determines if a given user is disabled, or is a work profile. 1324 * @param user The user to check 1325 * @return true if the user is disabled, or the user is a work profile 1326 */ isUserDisabledOrWorkProfile(UserHandle user)1327 public static boolean isUserDisabledOrWorkProfile(UserHandle user) { 1328 Application app = PermissionControllerApplication.get(); 1329 UserManager userManager = app.getSystemService(UserManager.class); 1330 // In android TV, parental control accounts are managed profiles 1331 return !userManager.getEnabledProfiles().contains(user) 1332 || (userManager.isManagedProfile(user.getIdentifier()) 1333 && !DeviceUtils.isTelevision(app)); 1334 } 1335 1336 /** 1337 * Get all the exempted packages. 1338 */ getExemptedPackages(@onNull RoleManager roleManager)1339 public static Set<String> getExemptedPackages(@NonNull RoleManager roleManager) { 1340 Set<String> exemptedPackages = new HashSet<>(); 1341 1342 exemptedPackages.add(SYSTEM_PKG); 1343 for (int i = 0; i < EXEMPTED_ROLES.length; i++) { 1344 exemptedPackages.addAll(roleManager.getRoleHolders(EXEMPTED_ROLES[i])); 1345 } 1346 1347 return exemptedPackages; 1348 } 1349 1350 /** 1351 * Get the timestamp and lastAccessType for the summary text 1352 * in app permission groups and permission apps screens 1353 * @return Triple<String, Integer, String> with the first being the formatted time 1354 * the second being lastAccessType and the third being the formatted date. 1355 */ getPermissionLastAccessSummaryTimestamp( Long lastAccessTime, Context context, String groupName)1356 public static Triple<String, Integer, String> getPermissionLastAccessSummaryTimestamp( 1357 Long lastAccessTime, Context context, String groupName) { 1358 long midnightToday = ZonedDateTime.now().truncatedTo(ChronoUnit.DAYS).toEpochSecond() 1359 * 1000L; 1360 long midnightYesterday = ZonedDateTime.now().minusDays(1).truncatedTo(ChronoUnit.DAYS) 1361 .toEpochSecond() * 1000L; 1362 long yesterdayAtThisTime = ZonedDateTime.now().minusDays(1).toEpochSecond() * 1000L; 1363 1364 boolean isLastAccessToday = lastAccessTime != null 1365 && midnightToday <= lastAccessTime; 1366 boolean isLastAccessWithinPast24h = lastAccessTime != null 1367 && yesterdayAtThisTime <= lastAccessTime; 1368 boolean isLastAccessTodayOrYesterday = lastAccessTime != null 1369 && midnightYesterday <= lastAccessTime; 1370 1371 String lastAccessTimeFormatted = ""; 1372 String lastAccessDateFormatted = ""; 1373 @AppPermsLastAccessType int lastAccessType = NOT_IN_LAST_7D; 1374 1375 if (lastAccessTime != null) { 1376 lastAccessTimeFormatted = DateFormat.getTimeFormat(context) 1377 .format(lastAccessTime); 1378 lastAccessDateFormatted = DateFormat.getDateFormat(context) 1379 .format(lastAccessTime); 1380 1381 if (!SENSOR_DATA_PERMISSIONS.contains(groupName)) { 1382 // For content providers we show either the last access is within 1383 // past 24 hours or past 7 days 1384 lastAccessType = isLastAccessWithinPast24h 1385 ? LAST_24H_CONTENT_PROVIDER : LAST_7D_CONTENT_PROVIDER; 1386 } else { 1387 // For sensor data permissions we show if the last access 1388 // is today, yesterday or older than yesterday 1389 lastAccessType = isLastAccessToday 1390 ? LAST_24H_SENSOR_TODAY : isLastAccessTodayOrYesterday 1391 ? LAST_24H_SENSOR_YESTERDAY : LAST_7D_SENSOR; 1392 } 1393 } 1394 1395 return new Triple<>(lastAccessTimeFormatted, lastAccessType, lastAccessDateFormatted); 1396 } 1397 1398 /** 1399 * Returns if the permission group is Camera or Microphone (status bar indicators). 1400 **/ isStatusBarIndicatorPermission(@onNull String permissionGroupName)1401 public static boolean isStatusBarIndicatorPermission(@NonNull String permissionGroupName) { 1402 return CAMERA.equals(permissionGroupName) || MICROPHONE.equals(permissionGroupName); 1403 } 1404 1405 /** 1406 * Navigate to notification settings for all apps 1407 * @param context The current Context 1408 */ navigateToNotificationSettings(@onNull Context context)1409 public static void navigateToNotificationSettings(@NonNull Context context) { 1410 Intent notificationIntent = new Intent(Settings.ACTION_ALL_APPS_NOTIFICATION_SETTINGS); 1411 context.startActivity(notificationIntent); 1412 } 1413 1414 /** 1415 * Navigate to notification settings for an app 1416 * @param context The current Context 1417 * @param packageName The package to navigate to 1418 * @param user Specifies the user of the package which should be navigated to. If null, the 1419 * current user is used. 1420 */ navigateToAppNotificationSettings(@onNull Context context, @NonNull String packageName, @NonNull UserHandle user)1421 public static void navigateToAppNotificationSettings(@NonNull Context context, 1422 @NonNull String packageName, @NonNull UserHandle user) { 1423 Intent notificationIntent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS); 1424 notificationIntent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName); 1425 context.startActivityAsUser(notificationIntent, user); 1426 } 1427 1428 /** 1429 * Returns if a card should be shown if the sensor is blocked 1430 **/ shouldDisplayCardIfBlocked(@onNull String permissionGroupName)1431 public static boolean shouldDisplayCardIfBlocked(@NonNull String permissionGroupName) { 1432 return DeviceConfig.getBoolean( 1433 DeviceConfig.NAMESPACE_PRIVACY, PROPERTY_WARNING_BANNER_DISPLAY_ENABLED, true) && ( 1434 CAMERA.equals(permissionGroupName) || MICROPHONE.equals(permissionGroupName) 1435 || LOCATION.equals(permissionGroupName)); 1436 } 1437 1438 /** 1439 * Returns the sensor code for a permission 1440 **/ 1441 @RequiresApi(Build.VERSION_CODES.S) getSensorCode(@onNull String permissionGroupName)1442 public static int getSensorCode(@NonNull String permissionGroupName) { 1443 return PERM_SENSOR_CODES.getOrDefault(permissionGroupName, -1); 1444 } 1445 1446 /** 1447 * Returns the blocked icon code for a permission 1448 **/ getBlockedIcon(@onNull String permissionGroupName)1449 public static int getBlockedIcon(@NonNull String permissionGroupName) { 1450 return PERM_BLOCKED_ICON.getOrDefault(permissionGroupName, -1); 1451 } 1452 1453 /** 1454 * Returns the blocked title code for a permission 1455 **/ getBlockedTitle(@onNull String permissionGroupName)1456 public static int getBlockedTitle(@NonNull String permissionGroupName) { 1457 return PERM_BLOCKED_TITLE.getOrDefault(permissionGroupName, -1); 1458 } 1459 1460 /** 1461 * Returns if the permission group has a background mode, even if the background mode is 1462 * introduced in a platform version after the one currently running 1463 **/ hasPermWithBackgroundModeCompat(LightAppPermGroup group)1464 public static boolean hasPermWithBackgroundModeCompat(LightAppPermGroup group) { 1465 if (SdkLevel.isAtLeastS()) { 1466 return group.getHasPermWithBackgroundMode(); 1467 } 1468 String groupName = group.getPermGroupName(); 1469 return group.getHasPermWithBackgroundMode() 1470 || Manifest.permission_group.CAMERA.equals(groupName) 1471 || Manifest.permission_group.MICROPHONE.equals(groupName); 1472 } 1473 1474 /** 1475 * Returns the appropriate enterprise string for the provided IDs 1476 */ 1477 @NonNull getEnterpriseString(@onNull Context context, @NonNull String updatableStringId, int defaultStringId, @NonNull Object... formatArgs)1478 public static String getEnterpriseString(@NonNull Context context, 1479 @NonNull String updatableStringId, int defaultStringId, @NonNull Object... formatArgs) { 1480 return SdkLevel.isAtLeastT() 1481 ? getUpdatableEnterpriseString( 1482 context, updatableStringId, defaultStringId, formatArgs) 1483 : context.getString(defaultStringId, formatArgs); 1484 } 1485 1486 @RequiresApi(Build.VERSION_CODES.TIRAMISU) 1487 @NonNull getUpdatableEnterpriseString(@onNull Context context, @NonNull String updatableStringId, int defaultStringId, @NonNull Object... formatArgs)1488 private static String getUpdatableEnterpriseString(@NonNull Context context, 1489 @NonNull String updatableStringId, int defaultStringId, @NonNull Object... formatArgs) { 1490 DevicePolicyManager dpm = getSystemServiceSafe(context, DevicePolicyManager.class); 1491 return dpm.getResources().getString(updatableStringId, () -> context.getString( 1492 defaultStringId, formatArgs), formatArgs); 1493 } 1494 } 1495