1 /*
2  * Copyright (C) 2017 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 android.content.pm;
18 
19 import static android.content.pm.SharedLibraryNames.ANDROID_TEST_BASE;
20 import static android.content.pm.SharedLibraryNames.ANDROID_TEST_MOCK;
21 import static android.content.pm.SharedLibraryNames.ANDROID_TEST_RUNNER;
22 import static android.content.pm.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;
23 
24 import android.content.pm.PackageParser.Package;
25 import android.util.Log;
26 
27 import com.android.internal.annotations.VisibleForTesting;
28 
29 import java.util.ArrayList;
30 import java.util.List;
31 import java.util.function.Supplier;
32 
33 /**
34  * Modifies {@link Package} in order to maintain backwards compatibility.
35  *
36  * @hide
37  */
38 @VisibleForTesting
39 public class PackageBackwardCompatibility extends PackageSharedLibraryUpdater {
40 
41     private static final String TAG = PackageBackwardCompatibility.class.getSimpleName();
42 
43     private static final PackageBackwardCompatibility INSTANCE;
44 
45     static {
46         final List<PackageSharedLibraryUpdater> packageUpdaters = new ArrayList<>();
47 
48         // Attempt to load and add the optional updater that will only be available when
49         // REMOVE_OAHL_FROM_BCP=true. If that could not be found then add the default updater that
50         // will remove any references to org.apache.http.library from the package so that it does
51         // not try and load the library when it is on the bootclasspath.
52         boolean bootClassPathContainsOAHL = !addOptionalUpdater(packageUpdaters,
53                 "android.content.pm.OrgApacheHttpLegacyUpdater",
54                 RemoveUnnecessaryOrgApacheHttpLegacyLibrary::new);
55 
56         // Add this before adding AndroidTestBaseUpdater so that android.test.base comes before
57         // android.test.mock.
packageUpdaters.add(new AndroidTestRunnerSplitUpdater())58         packageUpdaters.add(new AndroidTestRunnerSplitUpdater());
59 
60         // Attempt to load and add the optional updater that will only be available when
61         // REMOVE_ATB_FROM_BCP=true. If that could not be found then add the default updater that
62         // will remove any references to org.apache.http.library from the package so that it does
63         // not try and load the library when it is on the bootclasspath.
64         boolean bootClassPathContainsATB = !addOptionalUpdater(packageUpdaters,
65                 "android.content.pm.AndroidTestBaseUpdater",
66                 RemoveUnnecessaryAndroidTestBaseLibrary::new);
67 
68         PackageSharedLibraryUpdater[] updaterArray = packageUpdaters
69                 .toArray(new PackageSharedLibraryUpdater[0]);
70         INSTANCE = new PackageBackwardCompatibility(
71                 bootClassPathContainsOAHL, bootClassPathContainsATB, updaterArray);
72     }
73 
74     /**
75      * Add an optional {@link PackageSharedLibraryUpdater} instance to the list, if it could not be
76      * found then add a default instance instead.
77      *
78      * @param packageUpdaters the list to update.
79      * @param className the name of the optional class.
80      * @param defaultUpdater the supplier of the default instance.
81      * @return true if the optional updater was added false otherwise.
82      */
addOptionalUpdater(List<PackageSharedLibraryUpdater> packageUpdaters, String className, Supplier<PackageSharedLibraryUpdater> defaultUpdater)83     private static boolean addOptionalUpdater(List<PackageSharedLibraryUpdater> packageUpdaters,
84             String className, Supplier<PackageSharedLibraryUpdater> defaultUpdater) {
85         Class<? extends PackageSharedLibraryUpdater> clazz;
86         try {
87             clazz = (PackageBackwardCompatibility.class.getClassLoader()
88                     .loadClass(className)
89                     .asSubclass(PackageSharedLibraryUpdater.class));
90             Log.i(TAG, "Loaded " + className);
91         } catch (ClassNotFoundException e) {
92             Log.i(TAG, "Could not find " + className + ", ignoring");
93             clazz = null;
94         }
95 
96         boolean usedOptional = false;
97         PackageSharedLibraryUpdater updater;
98         if (clazz == null) {
99             updater = defaultUpdater.get();
100         } else {
101             try {
102                 updater = clazz.getConstructor().newInstance();
103                 usedOptional = true;
104             } catch (ReflectiveOperationException e) {
105                 throw new IllegalStateException("Could not create instance of " + className, e);
106             }
107         }
108         packageUpdaters.add(updater);
109         return usedOptional;
110     }
111 
112     @VisibleForTesting
getInstance()113     public static PackageSharedLibraryUpdater getInstance() {
114         return INSTANCE;
115     }
116 
117     private final boolean mBootClassPathContainsOAHL;
118 
119     private final boolean mBootClassPathContainsATB;
120 
121     private final PackageSharedLibraryUpdater[] mPackageUpdaters;
122 
PackageBackwardCompatibility(boolean bootClassPathContainsOAHL, boolean bootClassPathContainsATB, PackageSharedLibraryUpdater[] packageUpdaters)123     public PackageBackwardCompatibility(boolean bootClassPathContainsOAHL,
124             boolean bootClassPathContainsATB, PackageSharedLibraryUpdater[] packageUpdaters) {
125         this.mBootClassPathContainsOAHL = bootClassPathContainsOAHL;
126         this.mBootClassPathContainsATB = bootClassPathContainsATB;
127         this.mPackageUpdaters = packageUpdaters;
128     }
129 
130     /**
131      * Modify the shared libraries in the supplied {@link Package} to maintain backwards
132      * compatibility.
133      *
134      * @param pkg the {@link Package} to modify.
135      */
136     @VisibleForTesting
modifySharedLibraries(Package pkg)137     public static void modifySharedLibraries(Package pkg) {
138         INSTANCE.updatePackage(pkg);
139     }
140 
141     @Override
updatePackage(Package pkg)142     public void updatePackage(Package pkg) {
143         for (PackageSharedLibraryUpdater packageUpdater : mPackageUpdaters) {
144             packageUpdater.updatePackage(pkg);
145         }
146     }
147 
148     /**
149      * True if the org.apache.http.legacy is on the bootclasspath, false otherwise.
150      */
151     @VisibleForTesting
bootClassPathContainsOAHL()152     public static boolean bootClassPathContainsOAHL() {
153         return INSTANCE.mBootClassPathContainsOAHL;
154     }
155 
156     /**
157      * True if the android.test.base is on the bootclasspath, false otherwise.
158      */
159     @VisibleForTesting
bootClassPathContainsATB()160     public static boolean bootClassPathContainsATB() {
161         return INSTANCE.mBootClassPathContainsATB;
162     }
163 
164     /**
165      * Add android.test.mock dependency for any APK that depends on android.test.runner.
166      *
167      * <p>This is needed to maintain backwards compatibility as in previous versions of Android the
168      * android.test.runner library included the classes from android.test.mock which have since
169      * been split out into a separate library.
170      *
171      * @hide
172      */
173     @VisibleForTesting
174     public static class AndroidTestRunnerSplitUpdater extends PackageSharedLibraryUpdater {
175 
176         @Override
updatePackage(Package pkg)177         public void updatePackage(Package pkg) {
178             // android.test.runner has a dependency on android.test.mock so if android.test.runner
179             // is present but android.test.mock is not then add android.test.mock.
180             prefixImplicitDependency(pkg, ANDROID_TEST_RUNNER, ANDROID_TEST_MOCK);
181         }
182     }
183 
184     /**
185      * Remove any usages of org.apache.http.legacy from the shared library as the library is on the
186      * bootclasspath.
187      */
188     @VisibleForTesting
189     public static class RemoveUnnecessaryOrgApacheHttpLegacyLibrary
190             extends PackageSharedLibraryUpdater {
191 
192         @Override
updatePackage(Package pkg)193         public void updatePackage(Package pkg) {
194             removeLibrary(pkg, ORG_APACHE_HTTP_LEGACY);
195         }
196 
197     }
198 
199     /**
200      * Remove any usages of android.test.base from the shared library as the library is on the
201      * bootclasspath.
202      */
203     @VisibleForTesting
204     public static class RemoveUnnecessaryAndroidTestBaseLibrary
205             extends PackageSharedLibraryUpdater {
206 
207         @Override
updatePackage(Package pkg)208         public void updatePackage(Package pkg) {
209             removeLibrary(pkg, ANDROID_TEST_BASE);
210         }
211     }
212 }
213