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