1 /* 2 * Copyright (C) 2022 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.systemui.car.privacy; 18 19 import static android.os.UserHandle.USER_SYSTEM; 20 21 import android.Manifest; 22 import android.content.Context; 23 import android.content.pm.ApplicationInfo; 24 import android.content.pm.PackageManager; 25 import android.content.pm.UserInfo; 26 import android.os.UserHandle; 27 import android.os.UserManager; 28 import android.permission.PermissionGroupUsage; 29 import android.permission.PermissionManager; 30 import android.util.Log; 31 32 import androidx.annotation.Nullable; 33 import androidx.annotation.WorkerThread; 34 35 import com.android.systemui.privacy.PrivacyDialog; 36 import com.android.systemui.privacy.PrivacyItemController; 37 import com.android.systemui.privacy.PrivacyType; 38 import com.android.systemui.privacy.logging.PrivacyLogger; 39 import com.android.systemui.settings.UserTracker; 40 41 import java.util.ArrayList; 42 import java.util.Comparator; 43 import java.util.List; 44 import java.util.Map; 45 import java.util.Optional; 46 import java.util.stream.Collectors; 47 48 /** 49 * Implementation of {@link 50 * com.android.systemui.car.privacy.SensorQcPanel.SensorPrivacyElementsProvider} 51 */ 52 public abstract class PrivacyElementsProviderImpl implements 53 SensorQcPanel.SensorPrivacyElementsProvider { 54 private static final String TAG = "PrivacyElementsProviderImpl"; 55 private static final String EMPTY_APP_NAME = ""; 56 57 private static final Map<String, PrivacyType> PERM_GROUP_TO_PRIVACY_TYPE_MAP = 58 Map.of(Manifest.permission_group.CAMERA, PrivacyType.TYPE_CAMERA, 59 Manifest.permission_group.MICROPHONE, PrivacyType.TYPE_MICROPHONE, 60 Manifest.permission_group.LOCATION, PrivacyType.TYPE_LOCATION); 61 62 63 private final PermissionManager mPermissionManager; 64 private final UserTracker mUserTracker; 65 private final PrivacyLogger mPrivacyLogger; 66 private final PackageManager mPackageManager; 67 private final PrivacyItemController mPrivacyItemController; 68 private final UserManager mUserManager; 69 PrivacyElementsProviderImpl( Context context, PermissionManager permissionManager, PackageManager packageManager, PrivacyItemController privacyItemController, UserTracker userTracker, PrivacyLogger privacyLogger)70 public PrivacyElementsProviderImpl( 71 Context context, 72 PermissionManager permissionManager, 73 PackageManager packageManager, 74 PrivacyItemController privacyItemController, 75 UserTracker userTracker, 76 PrivacyLogger privacyLogger) { 77 mPermissionManager = permissionManager; 78 mPackageManager = packageManager; 79 mPrivacyItemController = privacyItemController; 80 mUserTracker = userTracker; 81 mPrivacyLogger = privacyLogger; 82 83 mUserManager = context.getSystemService(UserManager.class); 84 } 85 86 @Override getPrivacyElements()87 public List<PrivacyDialog.PrivacyElement> getPrivacyElements() { 88 List<PrivacyDialog.PrivacyElement> elements = filterAndSort(createPrivacyElements()); 89 mPrivacyLogger.logShowDialogContents(elements); 90 return elements; 91 } 92 getProviderPrivacyType()93 protected abstract PrivacyType getProviderPrivacyType(); 94 createPrivacyElements()95 private List<PrivacyDialog.PrivacyElement> createPrivacyElements() { 96 List<UserInfo> userInfos = mUserTracker.getUserProfiles(); 97 List<PermissionGroupUsage> permGroupUsages = getPermGroupUsages(); 98 mPrivacyLogger.logUnfilteredPermGroupUsage(permGroupUsages); 99 List<PrivacyDialog.PrivacyElement> items = new ArrayList<>(); 100 101 permGroupUsages.forEach(usage -> { 102 PrivacyType type = 103 verifyType(PERM_GROUP_TO_PRIVACY_TYPE_MAP.get(usage.getPermissionGroupName())); 104 if (type == null) return; 105 106 int userId = UserHandle.getUserId(usage.getUid()); 107 Optional<UserInfo> optionalUserInfo = userInfos.stream() 108 .filter(ui -> ui.id == userId) 109 .findFirst(); 110 if (!optionalUserInfo.isPresent() && userId != USER_SYSTEM) return; 111 112 UserInfo userInfo = 113 optionalUserInfo.orElseGet(() -> mUserManager.getUserInfo(USER_SYSTEM)); 114 115 String appName = usage.isPhoneCall() 116 ? EMPTY_APP_NAME 117 : getLabelForPackage(usage.getPackageName(), usage.getUid()); 118 119 items.add( 120 new PrivacyDialog.PrivacyElement( 121 type, 122 usage.getPackageName(), 123 userId, 124 appName, 125 usage.getAttributionTag(), 126 /* attributionLabel= */ null, 127 usage.getProxyLabel(), 128 usage.getLastAccessTimeMillis(), 129 usage.isActive(), 130 userInfo.isManagedProfile(), 131 usage.isPhoneCall(), 132 usage.getPermissionGroupName(), 133 /* navigationIntent= */ null) 134 ); 135 }); 136 137 return items; 138 } 139 getApplicationInfo(String packageName, int userId)140 private Optional<ApplicationInfo> getApplicationInfo(String packageName, int userId) { 141 ApplicationInfo applicationInfo; 142 try { 143 applicationInfo = mPackageManager 144 .getApplicationInfoAsUser(packageName, /* flags= */ 0, userId); 145 return Optional.of(applicationInfo); 146 } catch (PackageManager.NameNotFoundException e) { 147 Log.w(TAG, "Application info not found for: " + packageName); 148 return Optional.empty(); 149 } 150 } 151 152 @WorkerThread getPermGroupUsages()153 private List<PermissionGroupUsage> getPermGroupUsages() { 154 return mPermissionManager.getIndicatorAppOpUsageData(); 155 } 156 157 @WorkerThread getLabelForPackage(String packageName, int userId)158 private String getLabelForPackage(String packageName, int userId) { 159 Optional<ApplicationInfo> applicationInfo = getApplicationInfo(packageName, userId); 160 161 if (!applicationInfo.isPresent()) return packageName; 162 163 return (String) applicationInfo.get().loadLabel(mPackageManager); 164 } 165 166 /** 167 * If {@link PrivacyType} is available then returns the argument, or else returns {@code null}. 168 */ 169 @Nullable verifyType(PrivacyType type)170 private PrivacyType verifyType(PrivacyType type) { 171 if ((type == PrivacyType.TYPE_CAMERA || type == PrivacyType.TYPE_MICROPHONE) 172 && mPrivacyItemController.getMicCameraAvailable()) { 173 return type; 174 } else if (type == PrivacyType.TYPE_LOCATION 175 && mPrivacyItemController.getLocationAvailable()) { 176 return type; 177 } else { 178 return null; 179 } 180 } 181 filterAndSort( List<PrivacyDialog.PrivacyElement> list)182 private List<PrivacyDialog.PrivacyElement> filterAndSort( 183 List<PrivacyDialog.PrivacyElement> list) { 184 return list.stream() 185 .filter(it -> it.getType() == getProviderPrivacyType()) 186 .sorted(new PrivacyElementComparator()) 187 .collect(Collectors.toList()); 188 } 189 190 private static class PrivacyElementComparator 191 implements Comparator<PrivacyDialog.PrivacyElement> { 192 @Override compare(PrivacyDialog.PrivacyElement it1, PrivacyDialog.PrivacyElement it2)193 public int compare(PrivacyDialog.PrivacyElement it1, PrivacyDialog.PrivacyElement it2) { 194 if (it1.getActive() && !it2.getActive()) { 195 return 1; 196 } else if (!it1.getActive() && it2.getActive()) { 197 return -1; 198 } else { 199 return Long.compare(it1.getLastActiveTimestamp(), it2.getLastActiveTimestamp()); 200 } 201 } 202 } 203 } 204