1 /* 2 * Copyright (C) 2011 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.server.pm; 18 19 import android.annotation.Nullable; 20 import android.content.pm.ApplicationInfo; 21 import android.content.pm.parsing.component.ParsedProcess; 22 import android.service.pm.PackageServiceDumpProto; 23 import android.util.ArrayMap; 24 import android.util.ArraySet; 25 import android.util.proto.ProtoOutputStream; 26 27 import com.android.internal.util.ArrayUtils; 28 import com.android.server.pm.parsing.pkg.AndroidPackage; 29 import com.android.server.utils.SnapshotCache; 30 31 import libcore.util.EmptyArray; 32 33 import java.util.ArrayList; 34 import java.util.List; 35 import java.util.Map; 36 37 /** 38 * Settings data for a particular shared user ID we know about. 39 */ 40 public final class SharedUserSetting extends SettingBase { 41 final String name; 42 43 int userId; 44 45 // flags that are associated with this uid, regardless of any package flags 46 int uidFlags; 47 int uidPrivateFlags; 48 49 // The lowest targetSdkVersion of all apps in the sharedUserSetting, used to assign seinfo so 50 // that all apps within the sharedUser run in the same selinux context. 51 int seInfoTargetSdkVersion; 52 53 final ArraySet<PackageSetting> packages; 54 55 final PackageSignatures signatures = new PackageSignatures(); 56 Boolean signaturesChanged; 57 58 final ArrayMap<String, ParsedProcess> processes; 59 60 /** 61 * Snapshot support. 62 */ 63 private final SnapshotCache<SharedUserSetting> mSnapshot; 64 makeCache()65 private SnapshotCache<SharedUserSetting> makeCache() { 66 return new SnapshotCache<SharedUserSetting>(this, this) { 67 @Override 68 public SharedUserSetting createSnapshot() { 69 return new SharedUserSetting(mSource); 70 }}; 71 } 72 73 SharedUserSetting(String _name, int _pkgFlags, int _pkgPrivateFlags) { 74 super(_pkgFlags, _pkgPrivateFlags); 75 uidFlags = _pkgFlags; 76 uidPrivateFlags = _pkgPrivateFlags; 77 name = _name; 78 seInfoTargetSdkVersion = android.os.Build.VERSION_CODES.CUR_DEVELOPMENT; 79 packages = new ArraySet<>(); 80 processes = new ArrayMap<>(); 81 mSnapshot = makeCache(); 82 } 83 84 // The copy constructor is used to create a snapshot 85 private SharedUserSetting(SharedUserSetting orig) { 86 super(orig); 87 name = orig.name; 88 uidFlags = orig.uidFlags; 89 uidPrivateFlags = orig.uidPrivateFlags; 90 packages = new ArraySet(orig.packages); 91 // A PackageParser.SigningDetails seems to consist solely of final attributes, so 92 // it is safe to copy the reference. 93 signatures.mSigningDetails = orig.signatures.mSigningDetails; 94 signaturesChanged = orig.signaturesChanged; 95 processes = new ArrayMap(orig.processes); 96 mSnapshot = new SnapshotCache.Sealed(); 97 } 98 99 /** 100 * Return a read-only snapshot of this object. 101 */ 102 public SharedUserSetting snapshot() { 103 return mSnapshot.snapshot(); 104 } 105 106 @Override 107 public String toString() { 108 return "SharedUserSetting{" + Integer.toHexString(System.identityHashCode(this)) + " " 109 + name + "/" + userId + "}"; 110 } 111 112 public void dumpDebug(ProtoOutputStream proto, long fieldId) { 113 long token = proto.start(fieldId); 114 proto.write(PackageServiceDumpProto.SharedUserProto.UID, userId); 115 proto.write(PackageServiceDumpProto.SharedUserProto.NAME, name); 116 proto.end(token); 117 } 118 119 void addProcesses(Map<String, ParsedProcess> newProcs) { 120 if (newProcs != null) { 121 final int numProcs = newProcs.size(); 122 for (String key : newProcs.keySet()) { 123 ParsedProcess newProc = newProcs.get(key); 124 ParsedProcess proc = processes.get(newProc.getName()); 125 if (proc == null) { 126 proc = new ParsedProcess(newProc); 127 processes.put(newProc.getName(), proc); 128 } else { 129 proc.addStateFrom(newProc); 130 } 131 } 132 onChanged(); 133 } 134 } 135 136 boolean removePackage(PackageSetting packageSetting) { 137 if (!packages.remove(packageSetting)) { 138 return false; 139 } 140 // recalculate the pkgFlags for this shared user if needed 141 if ((this.pkgFlags & packageSetting.pkgFlags) != 0) { 142 int aggregatedFlags = uidFlags; 143 for (PackageSetting ps : packages) { 144 aggregatedFlags |= ps.pkgFlags; 145 } 146 setFlags(aggregatedFlags); 147 } 148 if ((this.pkgPrivateFlags & packageSetting.pkgPrivateFlags) != 0) { 149 int aggregatedPrivateFlags = uidPrivateFlags; 150 for (PackageSetting ps : packages) { 151 aggregatedPrivateFlags |= ps.pkgPrivateFlags; 152 } 153 setPrivateFlags(aggregatedPrivateFlags); 154 } 155 // recalculate processes. 156 updateProcesses(); 157 onChanged(); 158 return true; 159 } 160 161 void addPackage(PackageSetting packageSetting) { 162 // If this is the first package added to this shared user, temporarily (until next boot) use 163 // its targetSdkVersion when assigning seInfo for the shared user. 164 if ((packages.size() == 0) && (packageSetting.pkg != null)) { 165 seInfoTargetSdkVersion = packageSetting.pkg.getTargetSdkVersion(); 166 } 167 if (packages.add(packageSetting)) { 168 setFlags(this.pkgFlags | packageSetting.pkgFlags); 169 setPrivateFlags(this.pkgPrivateFlags | packageSetting.pkgPrivateFlags); 170 onChanged(); 171 } 172 if (packageSetting.pkg != null) { 173 addProcesses(packageSetting.pkg.getProcesses()); 174 } 175 } 176 177 public @Nullable List<AndroidPackage> getPackages() { 178 if (packages == null || packages.size() == 0) { 179 return null; 180 } 181 final ArrayList<AndroidPackage> pkgList = new ArrayList<>(packages.size()); 182 for (PackageSetting ps : packages) { 183 if ((ps == null) || (ps.pkg == null)) { 184 continue; 185 } 186 pkgList.add(ps.pkg); 187 } 188 return pkgList; 189 } 190 191 public boolean isPrivileged() { 192 return (this.pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0; 193 } 194 195 /** 196 * Determine the targetSdkVersion for a sharedUser and update pkg.applicationInfo.seInfo 197 * to ensure that all apps within the sharedUser share an SELinux domain. Use the lowest 198 * targetSdkVersion of all apps within the shared user, which corresponds to the least 199 * restrictive selinux domain. 200 */ 201 public void fixSeInfoLocked() { 202 if (packages == null || packages.size() == 0) { 203 return; 204 } 205 for (PackageSetting ps : packages) { 206 if ((ps == null) || (ps.pkg == null)) { 207 continue; 208 } 209 if (ps.pkg.getTargetSdkVersion() < seInfoTargetSdkVersion) { 210 seInfoTargetSdkVersion = ps.pkg.getTargetSdkVersion(); 211 onChanged(); 212 } 213 } 214 215 for (PackageSetting ps : packages) { 216 if ((ps == null) || (ps.pkg == null)) { 217 continue; 218 } 219 final boolean isPrivileged = isPrivileged() | ps.pkg.isPrivileged(); 220 ps.getPkgState().setOverrideSeInfo(SELinuxMMAC.getSeInfo(ps.pkg, isPrivileged, 221 seInfoTargetSdkVersion)); 222 onChanged(); 223 } 224 } 225 226 /** 227 * Update tracked data about processes based on all known packages in the shared user ID. 228 */ 229 public void updateProcesses() { 230 processes.clear(); 231 for (int i = packages.size() - 1; i >= 0; i--) { 232 final AndroidPackage pkg = packages.valueAt(i).pkg; 233 if (pkg != null) { 234 addProcesses(pkg.getProcesses()); 235 } 236 } 237 } 238 239 /** Returns userIds which doesn't have any packages with this sharedUserId */ 240 public int[] getNotInstalledUserIds() { 241 int[] excludedUserIds = null; 242 for (PackageSetting ps : packages) { 243 final int[] userIds = ps.getNotInstalledUserIds(); 244 if (excludedUserIds == null) { 245 excludedUserIds = userIds; 246 } else { 247 for (int userId : excludedUserIds) { 248 if (!ArrayUtils.contains(userIds, userId)) { 249 excludedUserIds = ArrayUtils.removeInt(excludedUserIds, userId); 250 } 251 } 252 } 253 } 254 return excludedUserIds == null ? EmptyArray.INT : excludedUserIds; 255 } 256 257 /** Updates all fields in this shared user setting from another. */ 258 public SharedUserSetting updateFrom(SharedUserSetting sharedUser) { 259 copyFrom(sharedUser); 260 this.userId = sharedUser.userId; 261 this.uidFlags = sharedUser.uidFlags; 262 this.uidPrivateFlags = sharedUser.uidPrivateFlags; 263 this.seInfoTargetSdkVersion = sharedUser.seInfoTargetSdkVersion; 264 this.packages.clear(); 265 this.packages.addAll(sharedUser.packages); 266 this.signaturesChanged = sharedUser.signaturesChanged; 267 if (sharedUser.processes != null) { 268 final int numProcs = sharedUser.processes.size(); 269 this.processes.clear(); 270 this.processes.ensureCapacity(numProcs); 271 for (int i = 0; i < numProcs; i++) { 272 ParsedProcess proc = 273 new ParsedProcess(sharedUser.processes.valueAt(i)); 274 this.processes.put(proc.getName(), proc); 275 } 276 } else { 277 this.processes.clear(); 278 } 279 onChanged(); 280 return this; 281 } 282 } 283