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