1 /* 2 * Copyright 2019 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.PackageInstaller; 21 22 import com.android.internal.util.Preconditions; 23 24 import java.util.Objects; 25 26 /** 27 * Immutable class holding information about where the request to install or update an app 28 * came from. 29 */ 30 public final class InstallSource { 31 /** 32 * An instance of InstallSource representing an absence of knowledge of the source of 33 * a package. Used in preference to null. 34 */ 35 static final InstallSource EMPTY = new InstallSource(null, null, null, null, false, false, 36 null, PackageInstaller.PACKAGE_SOURCE_UNSPECIFIED); 37 38 /** We also memoize this case because it is common - all un-updated system apps. */ 39 private static final InstallSource EMPTY_ORPHANED = new InstallSource( 40 null, null, null, null, true, false, null, 41 PackageInstaller.PACKAGE_SOURCE_UNSPECIFIED); 42 43 /** 44 * The package that requested the installation, if known. May not correspond to a currently 45 * installed package if {@link #isInitiatingPackageUninstalled} is true. 46 */ 47 @Nullable 48 final String initiatingPackageName; 49 50 /** 51 * The signing details of the initiating package, if known. Always null if 52 * {@link #initiatingPackageName} is null. 53 */ 54 @Nullable 55 final PackageSignatures initiatingPackageSignatures; 56 57 /** 58 * The package on behalf of which the initiating package requested the installation, if any. 59 * For example if a downloaded APK is installed via the Package Installer this could be the 60 * app that performed the download. This value is provided by the initiating package and not 61 * verified by the framework. 62 */ 63 @Nullable 64 final String originatingPackageName; 65 66 /** 67 * Package name of the app that installed this package (the installer of record). Note that 68 * this may be modified. 69 */ 70 @Nullable 71 final String installerPackageName; 72 73 74 /** 75 * {@link android.content.Context#getAttributionTag()} of installing context. 76 */ 77 @Nullable 78 final String installerAttributionTag; 79 80 /** Indicates if the package that was the installerPackageName has been uninstalled. */ 81 final boolean isOrphaned; 82 83 /** 84 * Indicates if the package in initiatingPackageName has been uninstalled. Always false if 85 * {@link #initiatingPackageName} is null. 86 */ 87 final boolean isInitiatingPackageUninstalled; 88 89 final int packageSource; 90 create(@ullable String initiatingPackageName, @Nullable String originatingPackageName, @Nullable String installerPackageName, @Nullable String installerAttributionTag)91 static InstallSource create(@Nullable String initiatingPackageName, 92 @Nullable String originatingPackageName, @Nullable String installerPackageName, 93 @Nullable String installerAttributionTag) { 94 return create(initiatingPackageName, originatingPackageName, installerPackageName, 95 installerAttributionTag, PackageInstaller.PACKAGE_SOURCE_UNSPECIFIED); 96 } 97 create(@ullable String initiatingPackageName, @Nullable String originatingPackageName, @Nullable String installerPackageName, @Nullable String installerAttributionTag, boolean isOrphaned, boolean isInitiatingPackageUninstalled)98 static InstallSource create(@Nullable String initiatingPackageName, 99 @Nullable String originatingPackageName, @Nullable String installerPackageName, 100 @Nullable String installerAttributionTag, boolean isOrphaned, 101 boolean isInitiatingPackageUninstalled) { 102 return create(initiatingPackageName, originatingPackageName, installerPackageName, 103 installerAttributionTag, PackageInstaller.PACKAGE_SOURCE_UNSPECIFIED, isOrphaned, 104 isInitiatingPackageUninstalled); 105 } 106 create(@ullable String initiatingPackageName, @Nullable String originatingPackageName, @Nullable String installerPackageName, @Nullable String installerAttributionTag, int packageSource)107 static InstallSource create(@Nullable String initiatingPackageName, 108 @Nullable String originatingPackageName, @Nullable String installerPackageName, 109 @Nullable String installerAttributionTag, int packageSource) { 110 return create(initiatingPackageName, originatingPackageName, installerPackageName, 111 installerAttributionTag, packageSource, false, false); 112 } 113 create(@ullable String initiatingPackageName, @Nullable String originatingPackageName, @Nullable String installerPackageName, @Nullable String installerAttributionTag, int packageSource, boolean isOrphaned, boolean isInitiatingPackageUninstalled)114 static InstallSource create(@Nullable String initiatingPackageName, 115 @Nullable String originatingPackageName, @Nullable String installerPackageName, 116 @Nullable String installerAttributionTag, int packageSource, boolean isOrphaned, 117 boolean isInitiatingPackageUninstalled) { 118 return createInternal( 119 intern(initiatingPackageName), 120 intern(originatingPackageName), 121 intern(installerPackageName), 122 installerAttributionTag, 123 packageSource, 124 isOrphaned, isInitiatingPackageUninstalled, null); 125 } 126 createInternal(@ullable String initiatingPackageName, @Nullable String originatingPackageName, @Nullable String installerPackageName, @Nullable String installerAttributionTag, int packageSource, boolean isOrphaned, boolean isInitiatingPackageUninstalled, @Nullable PackageSignatures initiatingPackageSignatures)127 private static InstallSource createInternal(@Nullable String initiatingPackageName, 128 @Nullable String originatingPackageName, @Nullable String installerPackageName, 129 @Nullable String installerAttributionTag, int packageSource, boolean isOrphaned, 130 boolean isInitiatingPackageUninstalled, 131 @Nullable PackageSignatures initiatingPackageSignatures) { 132 if (initiatingPackageName == null && originatingPackageName == null 133 && installerPackageName == null && initiatingPackageSignatures == null 134 && !isInitiatingPackageUninstalled) { 135 return isOrphaned ? EMPTY_ORPHANED : EMPTY; 136 } 137 return new InstallSource(initiatingPackageName, originatingPackageName, 138 installerPackageName, installerAttributionTag, isOrphaned, 139 isInitiatingPackageUninstalled, initiatingPackageSignatures, packageSource 140 ); 141 } 142 InstallSource(@ullable String initiatingPackageName, @Nullable String originatingPackageName, @Nullable String installerPackageName, @Nullable String installerAttributionTag, boolean isOrphaned, boolean isInitiatingPackageUninstalled, @Nullable PackageSignatures initiatingPackageSignatures, int packageSource)143 private InstallSource(@Nullable String initiatingPackageName, 144 @Nullable String originatingPackageName, @Nullable String installerPackageName, 145 @Nullable String installerAttributionTag, boolean isOrphaned, 146 boolean isInitiatingPackageUninstalled, 147 @Nullable PackageSignatures initiatingPackageSignatures, 148 int packageSource) { 149 if (initiatingPackageName == null) { 150 Preconditions.checkArgument(initiatingPackageSignatures == null); 151 Preconditions.checkArgument(!isInitiatingPackageUninstalled); 152 } 153 this.initiatingPackageName = initiatingPackageName; 154 this.originatingPackageName = originatingPackageName; 155 this.installerPackageName = installerPackageName; 156 this.installerAttributionTag = installerAttributionTag; 157 this.isOrphaned = isOrphaned; 158 this.isInitiatingPackageUninstalled = isInitiatingPackageUninstalled; 159 this.initiatingPackageSignatures = initiatingPackageSignatures; 160 this.packageSource = packageSource; 161 } 162 163 /** 164 * Return an InstallSource the same as this one except with the specified 165 * {@link #installerPackageName}. 166 */ setInstallerPackage(@ullable String installerPackageName)167 InstallSource setInstallerPackage(@Nullable String installerPackageName) { 168 if (Objects.equals(installerPackageName, this.installerPackageName)) { 169 return this; 170 } 171 return createInternal(initiatingPackageName, originatingPackageName, 172 intern(installerPackageName), installerAttributionTag, packageSource, isOrphaned, 173 isInitiatingPackageUninstalled, initiatingPackageSignatures); 174 } 175 176 /** 177 * Return an InstallSource the same as this one except with the specified value for 178 * {@link #isOrphaned}. 179 */ setIsOrphaned(boolean isOrphaned)180 InstallSource setIsOrphaned(boolean isOrphaned) { 181 if (isOrphaned == this.isOrphaned) { 182 return this; 183 } 184 return createInternal(initiatingPackageName, originatingPackageName, installerPackageName, 185 installerAttributionTag, packageSource, isOrphaned, isInitiatingPackageUninstalled, 186 initiatingPackageSignatures); 187 } 188 189 /** 190 * Return an InstallSource the same as this one except with the specified 191 * {@link #initiatingPackageSignatures}. 192 */ setInitiatingPackageSignatures(@ullable PackageSignatures signatures)193 InstallSource setInitiatingPackageSignatures(@Nullable PackageSignatures signatures) { 194 if (signatures == initiatingPackageSignatures) { 195 return this; 196 } 197 return createInternal(initiatingPackageName, originatingPackageName, installerPackageName, 198 installerAttributionTag, packageSource, isOrphaned, 199 isInitiatingPackageUninstalled, signatures); 200 } 201 202 /** 203 * Return an InstallSource the same as this one updated to reflect that the specified installer 204 * package name has been uninstalled. 205 */ removeInstallerPackage(@ullable String packageName)206 InstallSource removeInstallerPackage(@Nullable String packageName) { 207 if (packageName == null) { 208 return this; 209 } 210 211 boolean modified = false; 212 boolean isInitiatingPackageUninstalled = this.isInitiatingPackageUninstalled; 213 String originatingPackageName = this.originatingPackageName; 214 String installerPackageName = this.installerPackageName; 215 boolean isOrphaned = this.isOrphaned; 216 217 if (packageName.equals(this.initiatingPackageName)) { 218 if (!isInitiatingPackageUninstalled) { 219 // In this case we deliberately do not clear the package name (and signatures). 220 // We allow an app to retrieve details of its own install initiator even after 221 // it has been uninstalled. 222 isInitiatingPackageUninstalled = true; 223 modified = true; 224 } 225 } 226 if (packageName.equals(originatingPackageName)) { 227 originatingPackageName = null; 228 modified = true; 229 } 230 if (packageName.equals(installerPackageName)) { 231 installerPackageName = null; 232 isOrphaned = true; 233 modified = true; 234 } 235 236 if (!modified) { 237 return this; 238 } 239 240 return createInternal(initiatingPackageName, originatingPackageName, installerPackageName, 241 null, packageSource, isOrphaned, 242 isInitiatingPackageUninstalled, initiatingPackageSignatures); 243 } 244 245 @Nullable intern(@ullable String packageName)246 private static String intern(@Nullable String packageName) { 247 return packageName == null ? null : packageName.intern(); 248 } 249 } 250