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