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