1 /* 2 * Copyright (C) 2020 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.compat.sjp.cts; 18 19 import static android.compat.testing.Classpaths.ClasspathType.BOOTCLASSPATH; 20 import static android.compat.testing.Classpaths.ClasspathType.SYSTEMSERVERCLASSPATH; 21 22 import static com.google.common.truth.Truth.assertThat; 23 import static com.google.common.truth.Truth.assertWithMessage; 24 25 import static org.junit.Assume.assumeTrue; 26 27 import android.compat.testing.Classpaths; 28 import android.compat.testing.SharedLibraryInfo; 29 30 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper; 31 import com.android.modules.utils.build.testing.DeviceSdkLevel; 32 import com.android.tools.smali.dexlib2.iface.ClassDef; 33 import com.android.tradefed.device.DeviceNotAvailableException; 34 import com.android.tradefed.device.INativeDevice; 35 import com.android.tradefed.device.ITestDevice; 36 import com.android.tradefed.invoker.TestInformation; 37 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; 38 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; 39 import com.android.tradefed.testtype.junit4.BeforeClassWithInfo; 40 import com.android.tradefed.testtype.junit4.DeviceTestRunOptions; 41 import com.android.tradefed.util.FileUtil; 42 43 import com.google.common.collect.HashMultimap; 44 import com.google.common.collect.ImmutableCollection; 45 import com.google.common.collect.ImmutableList; 46 import com.google.common.collect.ImmutableMap; 47 import com.google.common.collect.ImmutableMultimap; 48 import com.google.common.collect.ImmutableSet; 49 import com.google.common.collect.ImmutableSetMultimap; 50 import com.google.common.collect.Multimap; 51 import com.google.common.collect.Multimaps; 52 53 import org.junit.Before; 54 import org.junit.Test; 55 import org.junit.runner.RunWith; 56 57 import java.io.File; 58 import java.io.IOException; 59 import java.util.Arrays; 60 import java.util.Collection; 61 import java.util.Optional; 62 import java.util.Set; 63 import java.util.jar.JarEntry; 64 import java.util.jar.JarFile; 65 import java.util.regex.Matcher; 66 import java.util.regex.Pattern; 67 import java.util.stream.Collectors; 68 import java.util.stream.Stream; 69 70 71 /** 72 * Tests for detecting no duplicate class files are present on BOOTCLASSPATH and 73 * SYSTEMSERVERCLASSPATH. 74 * 75 * <p>Duplicate class files are not safe as some of the jars on *CLASSPATH are updated outside of 76 * the main dessert release cycle; they also contribute to unnecessary disk space usage. 77 */ 78 @RunWith(DeviceJUnit4ClassRunner.class) 79 public class StrictJavaPackagesTest extends BaseHostJUnit4Test { 80 81 private static final String ANDROID_TEST_MOCK_JAR = "/system/framework/android.test.mock.jar"; 82 private static final String TEST_HELPER_PACKAGE = "android.compat.sjp.app"; 83 private static final String TEST_HELPER_APK = "StrictJavaPackagesTestApp.apk"; 84 85 private static final Pattern APEX_JAR_PATTERN = 86 Pattern.compile("\\/apex\\/(?<apexName>[^\\/]+)\\/.*\\.(jar|apk)"); 87 88 private static ImmutableList<String> sBootclasspathJars; 89 private static ImmutableList<String> sSystemserverclasspathJars; 90 private static ImmutableList<String> sSharedLibJars; 91 private static ImmutableList<SharedLibraryInfo> sSharedLibs; 92 private static ImmutableMultimap<String, String> sSharedLibsPathsToName; 93 private static ImmutableMultimap<String, String> sJarsToClasses; 94 private static ImmutableMultimap<String, String> sJarsToFiles; 95 96 private DeviceSdkLevel mDeviceSdkLevel; 97 98 /** 99 * This is the list of classes that are currently duplicated and should be addressed. 100 * 101 * <p> DO NOT ADD CLASSES TO THIS LIST! 102 */ 103 private static final Set<String> BCP_AND_SSCP_OVERLAP_BURNDOWN_LIST = 104 ImmutableSet.of( 105 "Landroid/annotation/AnimatorRes;", 106 "Landroid/annotation/AnimRes;", 107 "Landroid/annotation/AnyRes;", 108 "Landroid/annotation/AnyThread;", 109 "Landroid/annotation/AppIdInt;", 110 "Landroid/annotation/ArrayRes;", 111 "Landroid/annotation/AttrRes;", 112 "Landroid/annotation/BinderThread;", 113 "Landroid/annotation/BoolRes;", 114 "Landroid/annotation/BroadcastBehavior;", 115 "Landroid/annotation/BytesLong;", 116 "Landroid/annotation/CallbackExecutor;", 117 "Landroid/annotation/CallSuper;", 118 "Landroid/annotation/CheckResult;", 119 "Landroid/annotation/ColorInt;", 120 "Landroid/annotation/ColorLong;", 121 "Landroid/annotation/ColorRes;", 122 "Landroid/annotation/Condemned;", 123 "Landroid/annotation/CurrentTimeMillisLong;", 124 "Landroid/annotation/CurrentTimeSecondsLong;", 125 "Landroid/annotation/DimenRes;", 126 "Landroid/annotation/Dimension;", 127 "Landroid/annotation/Discouraged;", 128 "Landroid/annotation/DisplayContext;", 129 "Landroid/annotation/DrawableRes;", 130 "Landroid/annotation/DurationMillisLong;", 131 "Landroid/annotation/ElapsedRealtimeLong;", 132 "Landroid/annotation/EnforcePermission;", 133 "Landroid/annotation/FloatRange;", 134 "Landroid/annotation/FontRes;", 135 "Landroid/annotation/FractionRes;", 136 "Landroid/annotation/HalfFloat;", 137 "Landroid/annotation/Hide;", 138 "Landroid/annotation/IdRes;", 139 "Landroid/annotation/IntDef;", 140 "Landroid/annotation/IntegerRes;", 141 "Landroid/annotation/InterpolatorRes;", 142 "Landroid/annotation/IntRange;", 143 "Landroid/annotation/LayoutRes;", 144 "Landroid/annotation/LongDef;", 145 "Landroid/annotation/MainThread;", 146 "Landroid/annotation/MenuRes;", 147 "Landroid/annotation/NavigationRes;", 148 "Landroid/annotation/NonNull;", 149 "Landroid/annotation/NonUiContext;", 150 "Landroid/annotation/Nullable;", 151 "Landroid/annotation/PluralsRes;", 152 "Landroid/annotation/Px;", 153 "Landroid/annotation/RawRes;", 154 "Landroid/annotation/RequiresFeature;", 155 "Landroid/annotation/RequiresNoPermission;", 156 "Landroid/annotation/RequiresPermission;", 157 "Landroid/annotation/SdkConstant;", 158 "Landroid/annotation/Size;", 159 "Landroid/annotation/StringDef;", 160 "Landroid/annotation/StringRes;", 161 "Landroid/annotation/StyleableRes;", 162 "Landroid/annotation/StyleRes;", 163 "Landroid/annotation/SuppressAutoDoc;", 164 "Landroid/annotation/SuppressLint;", 165 "Landroid/annotation/SystemApi;", 166 "Landroid/annotation/SystemService;", 167 "Landroid/annotation/TargetApi;", 168 "Landroid/annotation/TestApi;", 169 "Landroid/annotation/TransitionRes;", 170 "Landroid/annotation/UiContext;", 171 "Landroid/annotation/UiThread;", 172 "Landroid/annotation/UptimeMillisLong;", 173 "Landroid/annotation/UserHandleAware;", 174 "Landroid/annotation/UserIdInt;", 175 "Landroid/annotation/Widget;", 176 "Landroid/annotation/WorkerThread;", 177 "Landroid/annotation/XmlRes;", 178 "Landroid/gsi/AvbPublicKey;", 179 "Landroid/gsi/GsiProgress;", 180 "Landroid/gsi/IGsiService;", 181 "Landroid/gsi/IGsiServiceCallback;", 182 "Landroid/gsi/IImageService;", 183 "Landroid/gsi/IProgressCallback;", 184 "Landroid/gsi/MappedImage;", 185 "Landroid/gui/TouchOcclusionMode;", 186 // TODO(b/227752875): contexthub V1 APIs can be removed 187 // from T+ with the fix in aosp/2050305. 188 "Landroid/hardware/contexthub/V1_0/AsyncEventType;", 189 "Landroid/hardware/contexthub/V1_0/ContextHub;", 190 "Landroid/hardware/contexthub/V1_0/ContextHubMsg;", 191 "Landroid/hardware/contexthub/V1_0/HostEndPoint;", 192 "Landroid/hardware/contexthub/V1_0/HubAppInfo;", 193 "Landroid/hardware/contexthub/V1_0/HubMemoryFlag;", 194 "Landroid/hardware/contexthub/V1_0/HubMemoryType;", 195 "Landroid/hardware/contexthub/V1_0/IContexthub;", 196 "Landroid/hardware/contexthub/V1_0/IContexthubCallback;", 197 "Landroid/hardware/contexthub/V1_0/MemRange;", 198 "Landroid/hardware/contexthub/V1_0/NanoAppBinary;", 199 "Landroid/hardware/contexthub/V1_0/NanoAppFlags;", 200 "Landroid/hardware/contexthub/V1_0/PhysicalSensor;", 201 "Landroid/hardware/contexthub/V1_0/Result;", 202 "Landroid/hardware/contexthub/V1_0/SensorType;", 203 "Landroid/hardware/contexthub/V1_0/TransactionResult;", 204 "Landroid/hardware/usb/gadget/V1_0/GadgetFunction;", 205 "Landroid/hardware/usb/gadget/V1_0/IUsbGadget;", 206 "Landroid/hardware/usb/gadget/V1_0/IUsbGadgetCallback;", 207 "Landroid/hardware/usb/gadget/V1_0/Status;", 208 "Landroid/os/IDumpstate;", 209 "Landroid/os/IDumpstateListener;", 210 "Landroid/os/IInstalld;", 211 "Landroid/os/IStoraged;", 212 "Landroid/os/IVold;", 213 "Landroid/os/IVoldListener;", 214 "Landroid/os/IVoldMountCallback;", 215 "Landroid/os/IVoldTaskListener;", 216 "Landroid/os/TouchOcclusionMode;", 217 "Landroid/os/storage/CrateMetadata;", 218 "Landroid/view/LayerMetadataKey;", 219 "Lcom/android/internal/annotations/CompositeRWLock;", 220 "Lcom/android/internal/annotations/GuardedBy;", 221 "Lcom/android/internal/annotations/Immutable;", 222 "Lcom/android/internal/annotations/VisibleForNative;", 223 "Lcom/android/internal/annotations/VisibleForTesting;", 224 // TODO(b/173649240): due to an oversight, some new overlaps slipped through 225 // in S. 226 "Landroid/hardware/usb/gadget/V1_1/IUsbGadget;", 227 "Landroid/hardware/usb/gadget/V1_2/GadgetFunction;", 228 "Landroid/hardware/usb/gadget/V1_2/IUsbGadget;", 229 "Landroid/hardware/usb/gadget/V1_2/IUsbGadgetCallback;", 230 "Landroid/hardware/usb/gadget/V1_2/UsbSpeed;", 231 "Landroid/os/CreateAppDataArgs;", 232 "Landroid/os/CreateAppDataResult;", 233 "Landroid/os/ReconcileSdkDataArgs;", 234 "Lcom/android/internal/util/FrameworkStatsLog;", 235 // Extra Pixel specific S oversights 236 "Landroid/os/BlockUntrustedTouchesMode;", 237 "Landroid/os/IInputConstants;", 238 "Landroid/os/InputEventInjectionResult;", 239 "Landroid/os/InputEventInjectionSync;", 240 // TODO(b/242741880): Remove duplication between sdksandbox-service and 241 // sdk-sandbox-framework 242 "Landroid/app/sdksandbox/ILoadSdkCallback;", 243 "Landroid/app/sdksandbox/IRequestSurfacePackageCallback;", 244 "Landroid/app/sdksandbox/ISdkSandboxManager;", 245 "Landroid/app/sdksandbox/ISdkSandboxLifecycleCallback;", 246 "Landroid/app/sdksandbox/ISdkSandboxProcessDeathCallback;", 247 "Landroid/app/sdksandbox/ISendDataCallback;", 248 "Landroid/app/sdksandbox/ISharedPreferencesSyncCallback;", 249 "Landroid/app/sdksandbox/ISdkToServiceCallback;", 250 "Landroid/app/sdksandbox/IUnloadSdkCallback;", 251 // b/325060980 : Remove duplication between telephony-common.jar and 252 // services.jar 253 "Lcom/android/server/updates/ConfigUpdateInstallReceiver;" 254 ); 255 256 private static final String FEATURE_WEARABLE = "android.hardware.type.watch"; 257 private static final String FEATURE_AUTOMOTIVE = "android.hardware.type.automotive"; 258 259 private static final Set<String> WEAR_HIDL_OVERLAP_BURNDOWN_LIST = 260 ImmutableSet.of( 261 "Landroid/hidl/base/V1_0/DebugInfo$Architecture;", 262 "Landroid/hidl/base/V1_0/IBase;", 263 "Landroid/hidl/base/V1_0/IBase$Proxy;", 264 "Landroid/hidl/base/V1_0/IBase$Stub;", 265 "Landroid/hidl/base/V1_0/DebugInfo;", 266 "Landroid/hidl/safe_union/V1_0/Monostate;" 267 ); 268 269 private static final Set<String> AUTOMOTIVE_HIDL_OVERLAP_BURNDOWN_LIST = 270 ImmutableSet.of( 271 "Landroid/hidl/base/V1_0/DebugInfo$Architecture;", 272 "Landroid/hidl/base/V1_0/IBase;", 273 "Landroid/hidl/base/V1_0/IBase$Proxy;", 274 "Landroid/hidl/base/V1_0/IBase$Stub;", 275 "Landroid/hidl/base/V1_0/DebugInfo;" 276 ); 277 278 /** 279 * TODO(b/199529199): Address these. 280 * List of duplicate classes between bootclasspath and shared libraries. 281 * 282 * <p> DO NOT ADD CLASSES TO THIS LIST! 283 */ 284 private static final Set<String> BCP_AND_SHARED_LIB_BURNDOWN_LIST = 285 ImmutableSet.of( 286 "Landroid/hidl/base/V1_0/DebugInfo;", 287 "Landroid/hidl/base/V1_0/IBase;", 288 "Landroid/hidl/manager/V1_0/IServiceManager;", 289 "Landroid/hidl/manager/V1_0/IServiceNotification;", 290 "Landroidx/annotation/Keep;", 291 "Lcom/google/android/embms/nano/EmbmsProtos;", 292 "Lcom/google/protobuf/nano/android/ParcelableExtendableMessageNano;", 293 "Lcom/google/protobuf/nano/android/ParcelableMessageNano;", 294 "Lcom/google/protobuf/nano/android/ParcelableMessageNanoCreator;", 295 "Lcom/google/protobuf/nano/CodedInputByteBufferNano;", 296 "Lcom/google/protobuf/nano/CodedOutputByteBufferNano;", 297 "Lcom/google/protobuf/nano/ExtendableMessageNano;", 298 "Lcom/google/protobuf/nano/Extension;", 299 "Lcom/google/protobuf/nano/FieldArray;", 300 "Lcom/google/protobuf/nano/FieldData;", 301 "Lcom/google/protobuf/nano/InternalNano;", 302 "Lcom/google/protobuf/nano/InvalidProtocolBufferNanoException;", 303 "Lcom/google/protobuf/nano/MapFactories;", 304 "Lcom/google/protobuf/nano/MessageNano;", 305 "Lcom/google/protobuf/nano/MessageNanoPrinter;", 306 "Lcom/google/protobuf/nano/UnknownFieldData;", 307 "Lcom/google/protobuf/nano/WireFormatNano;", 308 "Lcom/qualcomm/qcrilhook/BaseQmiTypes;", 309 "Lcom/qualcomm/qcrilhook/CSignalStrength;", 310 "Lcom/qualcomm/qcrilhook/EmbmsOemHook;", 311 "Lcom/qualcomm/qcrilhook/EmbmsProtoUtils;", 312 "Lcom/qualcomm/qcrilhook/IOemHookCallback;", 313 "Lcom/qualcomm/qcrilhook/IQcRilHook;", 314 "Lcom/qualcomm/qcrilhook/IQcRilHookExt;", 315 "Lcom/qualcomm/qcrilhook/OemHookCallback;", 316 "Lcom/qualcomm/qcrilhook/PresenceMsgBuilder;", 317 "Lcom/qualcomm/qcrilhook/PresenceMsgParser;", 318 "Lcom/qualcomm/qcrilhook/PresenceOemHook;", 319 "Lcom/qualcomm/qcrilhook/PrimitiveParser;", 320 "Lcom/qualcomm/qcrilhook/QcRilHook;", 321 "Lcom/qualcomm/qcrilhook/QcRilHookCallback;", 322 "Lcom/qualcomm/qcrilhook/QcRilHookCallbackExt;", 323 "Lcom/qualcomm/qcrilhook/QcRilHookExt;", 324 "Lcom/qualcomm/qcrilhook/QmiOemHook;", 325 "Lcom/qualcomm/qcrilhook/QmiOemHookConstants;", 326 "Lcom/qualcomm/qcrilhook/QmiPrimitiveTypes;", 327 "Lcom/qualcomm/qcrilhook/TunerOemHook;", 328 "Lcom/qualcomm/qcrilmsgtunnel/IQcrilMsgTunnel;", 329 "Lcom/qualcomm/utils/CommandException;", 330 "Lcom/qualcomm/utils/RILConstants;", 331 "Lorg/codeaurora/telephony/utils/CommandException;", 332 "Lorg/codeaurora/telephony/utils/Log;", 333 "Lorg/codeaurora/telephony/utils/RILConstants;", 334 "Lorg/chromium/net/ApiVersion;", 335 "Lorg/chromium/net/BidirectionalStream;", 336 "Lorg/chromium/net/CallbackException;", 337 "Lorg/chromium/net/CronetEngine;", 338 "Lorg/chromium/net/CronetException;", 339 "Lorg/chromium/net/CronetProvider;", 340 "Lorg/chromium/net/EffectiveConnectionType;", 341 "Lorg/chromium/net/ExperimentalBidirectionalStream;", 342 "Lorg/chromium/net/ExperimentalCronetEngine;", 343 "Lorg/chromium/net/ExperimentalUrlRequest;", 344 "Lorg/chromium/net/ICronetEngineBuilder;", 345 "Lorg/chromium/net/InlineExecutionProhibitedException;", 346 "Lorg/chromium/net/NetworkException;", 347 "Lorg/chromium/net/NetworkQualityRttListener;", 348 "Lorg/chromium/net/NetworkQualityThroughputListener;", 349 "Lorg/chromium/net/QuicException;", 350 "Lorg/chromium/net/RequestFinishedInfo;", 351 "Lorg/chromium/net/RttThroughputValues;", 352 "Lorg/chromium/net/ThreadStatsUid;", 353 "Lorg/chromium/net/UploadDataProvider;", 354 "Lorg/chromium/net/UploadDataProviders;", 355 "Lorg/chromium/net/UploadDataSink;", 356 "Lorg/chromium/net/UrlRequest;", 357 "Lorg/chromium/net/UrlResponseInfo;" 358 ); 359 private static final ImmutableSet<String> PERMISSION_CONTROLLER_APK_IN_APEX_BURNDOWN_LIST = 360 ImmutableSet.of( 361 "Lcom/android/modules/utils/build/SdkLevel;", 362 "Lcom/android/settingslib/RestrictedLockUtils;", 363 "Lcom/android/safetycenter/resources/SafetyCenterResourcesContext;" 364 ); 365 // TODO: b/223837599 366 private static final ImmutableSet<String> TETHERING_APK_IN_APEX_BURNDOWN_LIST = 367 ImmutableSet.of( 368 "Landroid/hidl/base/V1_0/DebugInfo;", 369 // /system/framework/services.jar 370 "Landroid/net/DataStallReportParcelable;", 371 "Landroid/net/DhcpResultsParcelable;", 372 "Landroid/net/INetd;", 373 "Landroid/net/INetdUnsolicitedEventListener;", 374 "Landroid/net/INetworkStackConnector;", 375 "Landroid/net/INetworkStackStatusCallback;", 376 "Landroid/net/InformationElementParcelable;", 377 "Landroid/net/InitialConfigurationParcelable;", 378 "Landroid/net/InterfaceConfigurationParcel;", 379 "Landroid/net/Layer2InformationParcelable;", 380 "Landroid/net/Layer2PacketParcelable;", 381 "Landroid/net/MarkMaskParcel;", 382 "Landroid/net/NativeNetworkConfig;", 383 "Landroid/net/NattKeepalivePacketDataParcelable;", 384 "Landroid/net/NetworkTestResultParcelable;", 385 "Landroid/net/PrivateDnsConfigParcel;", 386 "Landroid/net/ProvisioningConfigurationParcelable;", 387 "Landroid/net/RouteInfoParcel;", 388 "Landroid/net/ScanResultInfoParcelable;", 389 "Landroid/net/TcpKeepalivePacketDataParcelable;", 390 "Landroid/net/TetherConfigParcel;", 391 "Landroid/net/TetherOffloadRuleParcel;", 392 "Landroid/net/TetherStatsParcel;", 393 "Landroid/net/UidRangeParcel;", 394 "Landroid/net/dhcp/DhcpLeaseParcelable;", 395 "Landroid/net/dhcp/DhcpServingParamsParcel;", 396 "Landroid/net/dhcp/IDhcpEventCallbacks;", 397 "Landroid/net/dhcp/IDhcpServer;", 398 "Landroid/net/dhcp/IDhcpServerCallbacks;", 399 "Landroid/net/ipmemorystore/Blob;", 400 "Landroid/net/ipmemorystore/NetworkAttributesParcelable;", 401 "Landroid/net/ipmemorystore/SameL3NetworkResponseParcelable;", 402 "Landroid/net/ipmemorystore/StatusParcelable;", 403 "Landroid/net/netd/aidl/NativeUidRangeConfig;", 404 "Landroid/net/networkstack/aidl/NetworkMonitorParameters;", 405 "Landroid/net/networkstack/aidl/dhcp/DhcpOption;", 406 "Landroid/net/networkstack/aidl/ip/ReachabilityLossInfoParcelable;", 407 "Landroid/net/networkstack/aidl/quirks/IPv6ProvisioningLossQuirkParcelable;", 408 "Landroid/net/shared/NetdUtils;", 409 "Landroid/net/util/NetworkConstants;", 410 "Landroid/net/util/SharedLog;", 411 "Lcom/android/modules/utils/build/SdkLevel;", 412 ///system/framework/framework.jar 413 "Landroid/util/IndentingPrintWriter;", 414 "Lcom/android/internal/annotations/Keep;" 415 ); 416 // TODO: b/223836161 417 private static final ImmutableSet<String> EXTSERVICES_APK_IN_APEX_BURNDOWN_LIST = 418 ImmutableSet.of( 419 ///system/framework/framework.jar 420 "Landroid/view/displayhash/DisplayHashResultCallback;", 421 "Landroid/view/displayhash/DisplayHash;", 422 "Landroid/view/displayhash/VerifiedDisplayHash;" 423 ); 424 // TODO: b/223836163 425 private static final ImmutableSet<String> ODA_APK_IN_APEX_BURNDOWN_LIST = 426 ImmutableSet.of( 427 // /apex/com.android.ondevicepersonalization/javalib/framework-ondevicepersonalization.jar 428 "Landroid/ondevicepersonalization/aidl/IInitOnDevicePersonalizationCallback;", 429 "Landroid/ondevicepersonalization/aidl/IOnDevicePersonalizationManagerService;" 430 ); 431 // TODO: b/223837017 432 private static final ImmutableSet<String> CELLBROADCAST_APK_IN_APEX_BURNDOWN_LIST = 433 ImmutableSet.of( 434 // /system/framework/telephony-common.jar 435 "Lcom/android/cellbroadcastservice/CellBroadcastStatsLog;", 436 // /system/framework/framework.jar 437 "Lcom/android/internal/util/IState;", 438 "Lcom/android/internal/util/StateMachine;", 439 "Lcom/android/internal/util/State;" 440 ); 441 442 // TODO: b/234557765 443 private static final ImmutableSet<String> ADSERVICES_SANDBOX_APK_IN_APEX_BURNDOWN_LIST = 444 ImmutableSet.of( 445 // /apex/com.android.adservices/javalib/service-sdksandbox.jar 446 "Landroid/app/sdksandbox/ISharedPreferencesSyncCallback;", 447 "Lcom/android/sdksandbox/IDataReceivedCallback;", 448 "Lcom/android/sdksandbox/ILoadSdkInSandboxCallback;", 449 "Lcom/android/sdksandbox/IRequestSurfacePackageFromSdkCallback;", 450 "Lcom/android/sdksandbox/ISdkSandboxDisabledCallback;", 451 "Lcom/android/sdksandbox/ISdkSandboxManagerToSdkSandboxCallback;", 452 "Lcom/android/sdksandbox/ISdkSandboxService;", 453 "Lcom/android/sdksandbox/SandboxLatencyInfo-IA;", 454 "Lcom/android/sdksandbox/SandboxLatencyInfo;", 455 "Lcom/android/sdksandbox/IComputeSdkStorageCallback;" 456 ); 457 458 private static final ImmutableMap<String, ImmutableSet<String>> FULL_APK_IN_APEX_BURNDOWN = 459 new ImmutableMap.Builder<String, ImmutableSet<String>>() 460 .put("/apex/com.android.permission/priv-app/PermissionController/PermissionController.apk", 461 PERMISSION_CONTROLLER_APK_IN_APEX_BURNDOWN_LIST) 462 .put("/apex/com.android.permission/priv-app/GooglePermissionController/GooglePermissionController.apk", 463 PERMISSION_CONTROLLER_APK_IN_APEX_BURNDOWN_LIST) 464 .put("/apex/com.android.tethering/priv-app/InProcessTethering/InProcessTethering.apk", 465 TETHERING_APK_IN_APEX_BURNDOWN_LIST) 466 .put("/apex/com.android.tethering/priv-app/TetheringNextGoogle/TetheringNextGoogle.apk", 467 TETHERING_APK_IN_APEX_BURNDOWN_LIST) 468 .put("/apex/com.android.tethering/priv-app/TetheringGoogle/TetheringGoogle.apk", 469 TETHERING_APK_IN_APEX_BURNDOWN_LIST) 470 .put("/apex/com.android.tethering/priv-app/TetheringNext/TetheringNext.apk", 471 TETHERING_APK_IN_APEX_BURNDOWN_LIST) 472 .put("/apex/com.android.tethering/priv-app/Tethering/Tethering.apk", 473 TETHERING_APK_IN_APEX_BURNDOWN_LIST) 474 .put("/apex/com.android.extservices/priv-app/GoogleExtServices/GoogleExtServices.apk", 475 EXTSERVICES_APK_IN_APEX_BURNDOWN_LIST) 476 .put("/apex/com.android.extservices/priv-app/ExtServices/ExtServices.apk", 477 EXTSERVICES_APK_IN_APEX_BURNDOWN_LIST) 478 .put("/apex/com.android.ondevicepersonalization/app/OnDevicePersonalizationGoogle/OnDevicePersonalizationGoogle.apk", 479 ODA_APK_IN_APEX_BURNDOWN_LIST) 480 .put("/apex/com.android.ondevicepersonalization/app/OnDevicePersonalization/OnDevicePersonalization.apk", 481 ODA_APK_IN_APEX_BURNDOWN_LIST) 482 .put("/apex/com.android.cellbroadcast/priv-app/GoogleCellBroadcastServiceModule/GoogleCellBroadcastServiceModule.apk", 483 CELLBROADCAST_APK_IN_APEX_BURNDOWN_LIST) 484 .put("/apex/com.android.cellbroadcast/priv-app/CellBroadcastServiceModule/CellBroadcastServiceModule.apk", 485 CELLBROADCAST_APK_IN_APEX_BURNDOWN_LIST) 486 .put("/apex/com.android.adservices/app/SdkSandbox/SdkSandbox.apk", 487 ADSERVICES_SANDBOX_APK_IN_APEX_BURNDOWN_LIST) 488 .put("/apex/com.android.adservices/app/SdkSandboxGoogle/SdkSandboxGoogle.apk", 489 ADSERVICES_SANDBOX_APK_IN_APEX_BURNDOWN_LIST) 490 .build(); 491 492 // Bluetooth has not been updated on pre-u device 493 private static ImmutableSet<String> PRE_U_APK_IN_APEX_BLUETOOTH_BURNDOWN_LIST = 494 ImmutableSet.of( 495 // b/310322439 496 "Lcom/android/bluetooth/x/android/sysprop/AdbProperties;", 497 "Lcom/android/bluetooth/x/android/sysprop/ApkVerityProperties;", 498 "Lcom/android/bluetooth/x/android/sysprop/BluetoothProperties;", 499 "Lcom/android/bluetooth/x/android/sysprop/CarProperties;", 500 "Lcom/android/bluetooth/x/android/sysprop/ContactsProperties;", 501 "Lcom/android/bluetooth/x/android/sysprop/CryptoProperties;", 502 "Lcom/android/bluetooth/x/android/sysprop/DeviceProperties;", 503 "Lcom/android/bluetooth/x/android/sysprop/DisplayProperties;", 504 "Lcom/android/bluetooth/x/android/sysprop/HdmiProperties;", 505 "Lcom/android/bluetooth/x/android/sysprop/HypervisorProperties;", 506 "Lcom/android/bluetooth/x/android/sysprop/InputProperties;", 507 "Lcom/android/bluetooth/x/android/sysprop/MediaProperties;", 508 "Lcom/android/bluetooth/x/android/sysprop/NetworkProperties;", 509 "Lcom/android/bluetooth/x/android/sysprop/OtaProperties;", 510 "Lcom/android/bluetooth/x/android/sysprop/PowerProperties;", 511 "Lcom/android/bluetooth/x/android/sysprop/SetupWizardProperties;", 512 "Lcom/android/bluetooth/x/android/sysprop/SocProperties;", 513 "Lcom/android/bluetooth/x/android/sysprop/TelephonyProperties;", 514 "Lcom/android/bluetooth/x/android/sysprop/TraceProperties;", 515 "Lcom/android/bluetooth/x/android/sysprop/VndkProperties;", 516 "Lcom/android/bluetooth/x/android/sysprop/VoldProperties;", 517 "Lcom/android/bluetooth/x/android/sysprop/WifiProperties;"); 518 519 /** 520 * Lists of known failures when running testApkInApex_nonClasspathClasses against pre-U devices. 521 * 522 * <p>Add the new item into this list only if the failure is caused by base device image (not 523 * the mainline train). 524 */ 525 private static final ImmutableMap<String, ImmutableSet<String>> 526 PRE_U_APK_IN_APEX_BURNDOWN_LIST = 527 new ImmutableMap.Builder<String, ImmutableSet<String>>() 528 .put( 529 "/apex/com.android.btservices/app/BluetoothGoogle/BluetoothGoogle.apk", 530 PRE_U_APK_IN_APEX_BLUETOOTH_BURNDOWN_LIST) 531 .put( 532 "/apex/com.android.btservices/app/Bluetooth/Bluetooth.apk", 533 PRE_U_APK_IN_APEX_BLUETOOTH_BURNDOWN_LIST) 534 .build(); 535 536 /** 537 * Lists of known failures when running testApkInApex_nonClasspathClasses against pre-T devices. 538 * 539 * <p> Add the new item into this list only if the failure is caused by base device image (not the mainline train). 540 */ 541 private static final ImmutableMap<String, ImmutableSet<String>> PRE_T_APK_IN_APEX_BURNDOWN_LIST = 542 new ImmutableMap.Builder<String, ImmutableSet<String>>() 543 .put("/apex/com.android.cellbroadcast/priv-app/GoogleCellBroadcastServiceModule/GoogleCellBroadcastServiceModule.apk", 544 ImmutableSet.of( 545 // b/303732833 546 "Lcom/android/internal/util/Preconditions;" 547 )) 548 .build(); 549 550 /** 551 * Fetch all jar files in BCP, SSCP and shared libs and extract all the classes. 552 * 553 * <p>This method cannot be static, as there are no static equivalents for {@link #getDevice()} 554 * and {@link #getBuild()}. 555 */ 556 @BeforeClassWithInfo setupOnce(TestInformation testInfo)557 public static void setupOnce(TestInformation testInfo) throws Exception { 558 if (testInfo.getDevice() == null || testInfo.getBuildInfo() == null) { 559 throw new RuntimeException("No device and/or build type specified!"); 560 } 561 DeviceSdkLevel deviceSdkLevel = new DeviceSdkLevel(testInfo.getDevice()); 562 563 sBootclasspathJars = Classpaths.getJarsOnClasspath(testInfo.getDevice(), BOOTCLASSPATH); 564 sSystemserverclasspathJars = 565 Classpaths.getJarsOnClasspath(testInfo.getDevice(), SYSTEMSERVERCLASSPATH); 566 sSharedLibs = deviceSdkLevel.isDeviceAtLeastS() 567 ? Classpaths.getSharedLibraryInfos(testInfo.getDevice(), testInfo.getBuildInfo()) 568 : ImmutableList.of(); 569 sSharedLibJars = sSharedLibs.stream() 570 .map(sharedLibraryInfo -> sharedLibraryInfo.paths) 571 .flatMap(ImmutableCollection::stream) 572 .filter(file -> doesFileExist(file, testInfo.getDevice())) 573 // GmsCore should not contribute to *classpath. 574 .filter(file -> !file.contains("GmsCore")) 575 .filter(file -> !file.contains("com.google.android.gms")) 576 .collect(ImmutableList.toImmutableList()); 577 final ImmutableSetMultimap.Builder<String, String> sharedLibsPathsToName = 578 ImmutableSetMultimap.builder(); 579 sSharedLibs.forEach(sharedLibraryInfo -> { 580 sharedLibraryInfo.paths.forEach(path -> 581 sharedLibsPathsToName.putAll(path, sharedLibraryInfo.name)); 582 }); 583 sSharedLibsPathsToName = sharedLibsPathsToName.build(); 584 585 final ImmutableSetMultimap.Builder<String, String> jarsToFiles = 586 ImmutableSetMultimap.builder(); 587 final ImmutableSetMultimap.Builder<String, String> jarsToClasses = 588 ImmutableSetMultimap.builder(); 589 Stream.of(sBootclasspathJars.stream(), 590 sSystemserverclasspathJars.stream(), 591 sSharedLibJars.stream()) 592 .reduce(Stream::concat).orElseGet(Stream::empty) 593 .parallel() 594 .forEach(jarPath -> { 595 File jar = null; 596 try { 597 jar = pullJarFromDevice(testInfo.getDevice(), jarPath); 598 599 ImmutableSet<String> files = getJarFileContents(jar); 600 synchronized (jarsToFiles) { 601 jarsToFiles.putAll(jarPath, files); 602 } 603 604 ImmutableSet<String> classes = 605 Classpaths.getClassDefsFromJar(jar).stream() 606 .map(ClassDef::getType) 607 // Inner classes always go with their parent. 608 .filter(className -> !className.contains("$")) 609 .collect(ImmutableSet.toImmutableSet()); 610 synchronized (jarsToClasses) { 611 jarsToClasses.putAll(jarPath, classes); 612 } 613 } catch (DeviceNotAvailableException | IOException e) { 614 throw new RuntimeException(e); 615 } finally { 616 FileUtil.deleteFile(jar); 617 } 618 }); 619 sJarsToFiles = jarsToFiles.build(); 620 sJarsToClasses = jarsToClasses.build(); 621 } 622 623 @Before setup()624 public void setup() { 625 mDeviceSdkLevel = new DeviceSdkLevel(getDevice()); 626 } 627 628 /** 629 * Pretty prints a multimap to make it easier for a person to read it. 630 * 631 * It makes assumptions about the inputs: it assumes the keys are classes and the values are jar 632 * files where they exist. It also assumes that for any given class there will be 2 or more jar 633 * files where they are found. 634 * 635 * @return the string pretty formatted 636 */ prettyPrint(Multimap<String, String> classesToJars)637 private String prettyPrint(Multimap<String, String> classesToJars) { 638 if (classesToJars.isEmpty()) { 639 return "No findings"; 640 } 641 642 final HashMultimap<Collection<String>, String> jarsToClasses = HashMultimap.create(); 643 classesToJars.asMap().forEach((className, jarFiles) -> 644 jarsToClasses.put(jarFiles, className) 645 ); 646 647 StringBuilder sb = new StringBuilder(); 648 jarsToClasses.asMap().forEach((jars, classes) -> { 649 sb.append("The following jar files:\n"); 650 jars.forEach((jar) -> sb.append(" ").append(jar).append('\n')); 651 sb.append("Contain the following duplicate classes:\n"); 652 classes.forEach((klass) -> sb.append(" ").append(klass).append('\n')); 653 sb.append("End of duplications.\n\n"); 654 } 655 ); 656 sb.append("This can result in runtime breakages (now or in a future release)." 657 + " Read more at http://go/fixing-strict-java-packages\n"); 658 return sb.toString(); 659 } 660 661 /** 662 * Pretty prints a nested multimap to make it easier for a person to read it. 663 * 664 * It makes assumptions about the inputs: it assumes the outer keys are apk files (coming from 665 * APK in apexes) and the outer values are a Multimap with keys being a jar file and values 666 * classes that are defined in that jar and that also exist in the apk file. 667 * 668 * @return the string pretty formatted 669 */ prettyPrint( HashMultimap<String, Multimap<String, String>> apkToJarToClasses)670 private String prettyPrint( 671 HashMultimap<String, Multimap<String, String>> apkToJarToClasses) { 672 if (apkToJarToClasses.isEmpty()) { 673 return "No findings"; 674 } 675 StringBuilder sb = new StringBuilder(); 676 apkToJarToClasses.forEach((apk, jarToClasses) -> { 677 jarToClasses.asMap().forEach((jar, classes) -> { 678 sb.append("The apk in apex and jar file:\n"); 679 sb.append(" ").append(apk).append('\n'); 680 sb.append(" ").append(jar).append('\n'); 681 sb.append("contain the following duplicate class definitions:\n"); 682 classes.forEach(klass -> sb.append(" ").append(klass).append('\n')); 683 sb.append("End of duplications.\n\n"); 684 }); 685 }); 686 sb.append("This can result in runtime breakages (now or in a future release)." 687 + " Read more at http://go/fixing-strict-java-packages\n"); 688 return sb.toString(); 689 } 690 691 /** 692 * Ensure that there are no duplicate classes among jars listed in BOOTCLASSPATH. 693 */ 694 @Test testBootclasspath_nonDuplicateClasses()695 public void testBootclasspath_nonDuplicateClasses() throws Exception { 696 assumeTrue(mDeviceSdkLevel.isDeviceAtLeastT()); 697 assertThat(getDuplicateClasses(sBootclasspathJars)).isEmpty(); 698 } 699 700 /** 701 * Ensure that there are no duplicate classes among jars listed in SYSTEMSERVERCLASSPATH. 702 */ 703 @Test testSystemServerClasspath_nonDuplicateClasses()704 public void testSystemServerClasspath_nonDuplicateClasses() throws Exception { 705 assumeTrue(mDeviceSdkLevel.isDeviceAtLeastT()); 706 ImmutableSet<String> overlapBurndownList; 707 if (hasFeature(FEATURE_AUTOMOTIVE)) { 708 overlapBurndownList = ImmutableSet.copyOf(AUTOMOTIVE_HIDL_OVERLAP_BURNDOWN_LIST); 709 } else if (hasFeature(FEATURE_WEARABLE)) { 710 overlapBurndownList = ImmutableSet.copyOf(WEAR_HIDL_OVERLAP_BURNDOWN_LIST); 711 } else { 712 overlapBurndownList = ImmutableSet.of(); 713 } 714 Multimap<String, String> duplicates = getDuplicateClasses(sSystemserverclasspathJars); 715 Multimap<String, String> filtered = Multimaps.filterKeys(duplicates, 716 duplicate -> !overlapBurndownList.contains(duplicate)); 717 718 assertWithMessage(prettyPrint(filtered)) 719 .that(filtered) 720 .isEmpty(); 721 } 722 723 /** 724 * Ensure that there are no duplicate classes among jars listed in BOOTCLASSPATH and 725 * SYSTEMSERVERCLASSPATH. 726 */ 727 @Test testBootClasspathAndSystemServerClasspath_nonDuplicateClasses()728 public void testBootClasspathAndSystemServerClasspath_nonDuplicateClasses() throws Exception { 729 assumeTrue(mDeviceSdkLevel.isDeviceAtLeastT()); 730 ImmutableList.Builder<String> jars = ImmutableList.builder(); 731 jars.addAll(sBootclasspathJars); 732 jars.addAll(sSystemserverclasspathJars); 733 ImmutableSet<String> overlapBurndownList; 734 if (hasFeature(FEATURE_AUTOMOTIVE)) { 735 overlapBurndownList = ImmutableSet.<String>builder() 736 .addAll(BCP_AND_SSCP_OVERLAP_BURNDOWN_LIST) 737 .addAll(AUTOMOTIVE_HIDL_OVERLAP_BURNDOWN_LIST).build(); 738 } else if (hasFeature(FEATURE_WEARABLE)) { 739 overlapBurndownList = ImmutableSet.<String>builder() 740 .addAll(BCP_AND_SSCP_OVERLAP_BURNDOWN_LIST) 741 .addAll(WEAR_HIDL_OVERLAP_BURNDOWN_LIST).build(); 742 } else { 743 overlapBurndownList = ImmutableSet.copyOf(BCP_AND_SSCP_OVERLAP_BURNDOWN_LIST); 744 } 745 Multimap<String, String> duplicates = getDuplicateClasses(jars.build()); 746 Multimap<String, String> filtered = Multimaps.filterKeys(duplicates, 747 duplicate -> !overlapBurndownList.contains(duplicate) 748 && !jarsInSameApex(duplicates.get(duplicate))); 749 750 assertWithMessage(prettyPrint(filtered)) 751 .that(filtered) 752 .isEmpty(); 753 } 754 755 /** 756 * Ensure that there are no duplicate classes among APEX jars listed in BOOTCLASSPATH. 757 */ 758 @Test testBootClasspath_nonDuplicateApexJarClasses()759 public void testBootClasspath_nonDuplicateApexJarClasses() throws Exception { 760 Multimap<String, String> duplicates = getDuplicateClasses(sBootclasspathJars); 761 Multimap<String, String> filtered = 762 Multimaps.filterValues(duplicates, jar -> jar.startsWith("/apex/")); 763 764 assertWithMessage(prettyPrint(filtered)) 765 .that(filtered) 766 .isEmpty(); 767 } 768 769 /** 770 * Ensure that there are no duplicate classes among APEX jars listed in SYSTEMSERVERCLASSPATH. 771 */ 772 @Test testSystemServerClasspath_nonDuplicateApexJarClasses()773 public void testSystemServerClasspath_nonDuplicateApexJarClasses() throws Exception { 774 Multimap<String, String> duplicates = getDuplicateClasses(sSystemserverclasspathJars); 775 Multimap<String, String> filtered = 776 Multimaps.filterValues(duplicates, jar -> jar.startsWith("/apex/")); 777 778 assertWithMessage(prettyPrint(filtered)) 779 .that(filtered) 780 .isEmpty(); 781 } 782 783 /** 784 * Ensure that there are no duplicate classes among APEX jars listed in BOOTCLASSPATH and 785 * SYSTEMSERVERCLASSPATH. 786 */ 787 @Test testBootClasspathAndSystemServerClasspath_nonApexDuplicateClasses()788 public void testBootClasspathAndSystemServerClasspath_nonApexDuplicateClasses() 789 throws Exception { 790 ImmutableList.Builder<String> jars = ImmutableList.builder(); 791 jars.addAll(sBootclasspathJars); 792 jars.addAll(sSystemserverclasspathJars); 793 794 Multimap<String, String> duplicates = getDuplicateClasses(jars.build()); 795 Multimap<String, String> filtered = Multimaps.filterKeys(duplicates, 796 duplicate -> !BCP_AND_SSCP_OVERLAP_BURNDOWN_LIST.contains(duplicate)); 797 filtered = Multimaps.filterValues(filtered, jar -> jar.startsWith("/apex/")); 798 799 assertWithMessage(prettyPrint(filtered)) 800 .that(filtered) 801 .isEmpty(); 802 } 803 804 /** 805 * Ensure that there are no duplicate classes among jars listed in BOOTCLASSPATH and 806 * shared library jars. 807 */ 808 @Test testBootClasspathAndSharedLibs_nonDuplicateClasses()809 public void testBootClasspathAndSharedLibs_nonDuplicateClasses() throws Exception { 810 assumeTrue(mDeviceSdkLevel.isDeviceAtLeastS()); 811 final ImmutableList.Builder<String> jars = ImmutableList.builder(); 812 jars.addAll(sBootclasspathJars); 813 jars.addAll(sSharedLibJars); 814 final Multimap<String, String> duplicates = getDuplicateClasses(jars.build()); 815 final Multimap<String, String> filtered = Multimaps.filterKeys(duplicates, 816 dupeClass -> { 817 try { 818 final Collection<String> dupeJars = duplicates.get(dupeClass); 819 // Duplicate is already known. 820 if (BCP_AND_SHARED_LIB_BURNDOWN_LIST.contains(dupeClass)) { 821 return false; 822 } 823 // Duplicate is only between different versions of the same shared library. 824 if (isSameLibrary(dupeJars)) { 825 return false; 826 } 827 // Pre-T, the Android test mock library included some platform classes. 828 if (!mDeviceSdkLevel.isDeviceAtLeastT() 829 && dupeJars.contains(ANDROID_TEST_MOCK_JAR)) { 830 return false; 831 } 832 // Different versions of the same library may have different names, and 833 // there's 834 // no reliable way to dedupe them. Ignore duplicates if they do not 835 // include apex jars. 836 if (dupeJars.stream().noneMatch(lib -> lib.startsWith("/apex/"))) { 837 return false; 838 } 839 } catch (DeviceNotAvailableException e) { 840 throw new RuntimeException(e); 841 } 842 return true; 843 }); 844 assertWithMessage(prettyPrint(filtered)) 845 .that(filtered) 846 .isEmpty(); 847 } 848 849 /** 850 * Ensure that no apk-in-apex bundles classes that could be eclipsed by jars in 851 * BOOTCLASSPATH. 852 */ 853 @Test testApkInApex_nonClasspathClasses()854 public void testApkInApex_nonClasspathClasses() throws Exception { 855 HashMultimap<String, Multimap<String, String>> perApkClasspathDuplicates = 856 HashMultimap.create(); 857 Arrays.stream(collectApkInApexPaths()) 858 .filter(apk -> apk != null && !apk.isEmpty()) 859 .parallel() 860 .forEach(apk -> { 861 File apkFile = null; 862 try { 863 apkFile = pullJarFromDevice(getDevice(), apk); 864 final ImmutableSet<String> apkClasses = 865 Classpaths.getClassDefsFromJar(apkFile).stream() 866 .map(ClassDef::getType) 867 .collect(ImmutableSet.toImmutableSet()); 868 // b/226559955: The directory paths containing APKs contain the build ID, 869 // so strip out the @BUILD_ID portion. 870 // e.g. /apex/com.android.btservices/app/Bluetooth@SC-DEV/Bluetooth.apk -> 871 // /apex/com.android.btservices/app/Bluetooth/Bluetooth.apk 872 apk = apk.replaceFirst("@[^/]*", ""); 873 ImmutableSet<String> burndownClasses; 874 if (mDeviceSdkLevel.isDeviceAtLeastU()) { 875 burndownClasses = ImmutableSet.<String>builder() 876 .addAll(FULL_APK_IN_APEX_BURNDOWN.getOrDefault(apk, ImmutableSet.of())).build(); 877 } else if (mDeviceSdkLevel.isDeviceAtLeastT()) { 878 burndownClasses = ImmutableSet.<String>builder() 879 .addAll(FULL_APK_IN_APEX_BURNDOWN.getOrDefault(apk, ImmutableSet.of())) 880 .addAll(PRE_U_APK_IN_APEX_BURNDOWN_LIST.getOrDefault(apk, ImmutableSet.of())).build(); 881 } else { 882 // testApkInApex_nonClasspathClasses is not part of CTS until T 883 // therefore, running this for pre-T devices with additional list of known failures. 884 // Another option would be to skip this test entirely for pre-T devices. 885 burndownClasses = ImmutableSet.<String>builder() 886 .addAll(FULL_APK_IN_APEX_BURNDOWN.getOrDefault(apk, ImmutableSet.of())) 887 .addAll(PRE_T_APK_IN_APEX_BURNDOWN_LIST.getOrDefault(apk, ImmutableSet.of())).build(); 888 } 889 final Multimap<String, String> duplicates = 890 Multimaps.filterValues(sJarsToClasses, apkClasses::contains); 891 final Multimap<String, String> filteredDuplicates = 892 Multimaps.filterValues(duplicates, 893 className -> !burndownClasses.contains(className) 894 // TODO: b/225341497 895 && !className.equals("Landroidx/annotation/Keep;")); 896 final Multimap<String, String> bcpOnlyDuplicates = 897 Multimaps.filterKeys(filteredDuplicates, 898 sBootclasspathJars::contains); 899 if (!bcpOnlyDuplicates.isEmpty()) { 900 synchronized (perApkClasspathDuplicates) { 901 perApkClasspathDuplicates.put(apk, bcpOnlyDuplicates); 902 } 903 } 904 } catch (Exception e) { 905 throw new RuntimeException(e); 906 } finally { 907 FileUtil.deleteFile(apkFile); 908 } 909 }); 910 911 assertWithMessage(prettyPrint(perApkClasspathDuplicates)) 912 .that(perApkClasspathDuplicates) 913 .isEmpty(); 914 } 915 916 /** 917 * Ensure that there are no androidx dependencies in BOOTCLASSPATH, SYSTEMSERVERCLASSPATH 918 * and shared library jars. 919 */ 920 @Test testBootClasspathAndSystemServerClasspathAndSharedLibs_noAndroidxDependencies()921 public void testBootClasspathAndSystemServerClasspathAndSharedLibs_noAndroidxDependencies() 922 throws Exception { 923 assumeTrue(mDeviceSdkLevel.isDeviceAtLeastT()); 924 // WARNING: Do not add more exceptions here, no androidx should be in bootclasspath. 925 // See go/androidx-api-guidelines#module-naming for more details. 926 final ImmutableMap<String, ImmutableSet<String>> 927 LegacyExemptAndroidxSharedLibsNamesToClasses = 928 new ImmutableMap.Builder<String, ImmutableSet<String>>() 929 .put("androidx.camera.extensions.impl", 930 ImmutableSet.of("Landroidx/camera/extensions/impl/", 931 "Landroidx/camera/extensions/impl/advanced/", "Landroidx/annotation")) 932 .put("androidx.window.extensions", 933 ImmutableSet.of("Landroidx/window/common/", "Landroidx/window/extensions/", 934 "Landroidx/window/util/")) 935 .put("androidx.window.sidecar", 936 ImmutableSet.of("Landroidx/window/common/", "Landroidx/window/sidecar", 937 "Landroidx/window/util")) 938 .put("com.google.android.camera.experimental2019", 939 ImmutableSet.of("Landroidx/annotation")) 940 .put("com.google.android.camera.experimental2020_midyear", 941 ImmutableSet.of("Landroidx/annotation")) 942 .build(); 943 assertWithMessage("There must not be any androidx classes on the " 944 + "bootclasspath. Please use alternatives provided by the platform instead. " 945 + "See go/androidx-api-guidelines#module-naming.") 946 .that(sJarsToClasses.entries().stream() 947 .filter(e -> e.getKey().endsWith(".jar")) 948 .filter(e -> e.getValue().startsWith("Landroidx/")) 949 .filter(e -> !isLegacyAndroidxDependency( 950 LegacyExemptAndroidxSharedLibsNamesToClasses, e.getKey(), e.getValue())) 951 .collect(Collectors.toList()) 952 ).isEmpty(); 953 } 954 955 /** 956 * Ensure that there are no kotlin files in BOOTCLASSPATH, SYSTEMSERVERCLASSPATH 957 * and shared library jars. 958 */ 959 @Test testNoKotlinFilesInClasspaths()960 public void testNoKotlinFilesInClasspaths() throws Exception { 961 // This test was not in CTS until U. 962 assumeTrue(mDeviceSdkLevel.isDeviceAtLeastU()); 963 ImmutableList<String> kotlinFiles = 964 Stream.of(sBootclasspathJars.stream(), 965 sSystemserverclasspathJars.stream(), 966 sSharedLibJars.stream()) 967 .reduce(Stream::concat).orElseGet(Stream::empty) 968 .parallel() 969 .filter(jarPath -> { 970 // Exclude shared library apks. 971 return jarPath.endsWith(".jar") 972 && sJarsToFiles.get(jarPath) 973 .stream() 974 .anyMatch(file -> file.contains(".kotlin_builtins") 975 || file.contains(".kotlin_module")); 976 }) 977 .collect(ImmutableList.toImmutableList()); 978 assertThat(kotlinFiles).isEmpty(); 979 } 980 981 /** 982 * Ensure that all classes from protobuf libraries are jarjared before 983 * included in BOOTCLASSPATH, SYSTEMSERVERCLASSPATH and shared library jars 984 */ 985 @Test testNoProtobufClassesWithoutJarjar()986 public void testNoProtobufClassesWithoutJarjar() throws Exception { 987 assumeTrue(mDeviceSdkLevel.isDeviceAtLeastU()); 988 assertWithMessage("Classes from protobuf libraries must not be included in bootclasspath " 989 + "and systemserverclasspath without being jarjared.") 990 .that(Stream.of(sBootclasspathJars.stream(), 991 sSystemserverclasspathJars.stream(), 992 sSharedLibJars.stream()) 993 .reduce(Stream::concat).orElseGet(Stream::empty) 994 .parallel() 995 .filter(jarPath -> { 996 return sJarsToClasses 997 .get(jarPath) 998 .stream() 999 .anyMatch(cls -> cls.startsWith("Lcom/google/protobuf/")); 1000 }) 1001 .collect(ImmutableList.toImmutableList()) 1002 ).isEmpty(); 1003 } 1004 pullJarFromDevice(INativeDevice device, String remoteJarPath)1005 private static File pullJarFromDevice(INativeDevice device, 1006 String remoteJarPath) throws DeviceNotAvailableException { 1007 File jar = device.pullFile(remoteJarPath); 1008 if (jar == null) { 1009 throw new IllegalStateException("could not pull remote file " + remoteJarPath); 1010 } 1011 return jar; 1012 } 1013 getJarFileContents(File jar)1014 private static ImmutableSet<String> getJarFileContents(File jar) throws IOException { 1015 try (JarFile jarFile = new JarFile(jar)) { 1016 return jarFile.stream() 1017 .map(JarEntry::getName) 1018 .collect(ImmutableSet.toImmutableSet()); 1019 } 1020 } 1021 isLegacyAndroidxDependency( ImmutableMap<String, ImmutableSet<String>> legacyExemptAndroidxSharedLibsNamesToClasses, String path, String className)1022 private boolean isLegacyAndroidxDependency( 1023 ImmutableMap<String, ImmutableSet<String>> legacyExemptAndroidxSharedLibsNamesToClasses, 1024 String path, String className) { 1025 return sSharedLibsPathsToName.get(path).stream() 1026 .filter(legacyExemptAndroidxSharedLibsNamesToClasses::containsKey) 1027 .flatMap(name -> legacyExemptAndroidxSharedLibsNamesToClasses.get(name).stream()) 1028 .anyMatch(className::startsWith); 1029 } 1030 collectApkInApexPaths()1031 private String[] collectApkInApexPaths() { 1032 try { 1033 final CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild()); 1034 final String installError = getDevice().installPackage( 1035 buildHelper.getTestFile(TEST_HELPER_APK), false); 1036 assertWithMessage("Failed to install %s due to: %s", TEST_HELPER_APK, installError) 1037 .that(installError).isNull(); 1038 runDeviceTests(new DeviceTestRunOptions(TEST_HELPER_PACKAGE) 1039 .setDevice(getDevice()) 1040 .setTestClassName(TEST_HELPER_PACKAGE + ".ApexDeviceTest") 1041 .setTestMethodName("testCollectApkInApexPaths")); 1042 final String remoteFile = "/sdcard/apk-in-apex-paths.txt"; 1043 String content; 1044 try { 1045 content = getDevice().pullFileContents(remoteFile); 1046 } finally { 1047 getDevice().deleteFile(remoteFile); 1048 } 1049 return content.split("\n"); 1050 } catch (Exception e) { 1051 throw new RuntimeException(e); 1052 } finally { 1053 try { 1054 getDevice().uninstallPackage(TEST_HELPER_PACKAGE); 1055 } catch (Exception e) { 1056 throw new RuntimeException(e); 1057 } 1058 } 1059 } 1060 1061 /** 1062 * Gets the duplicate classes within a list of jar files. 1063 * 1064 * @param jars a list of jar files. 1065 * @return a multimap with the class name as a key and the jar files as a value. 1066 */ getDuplicateClasses(ImmutableCollection<String> jars)1067 private Multimap<String, String> getDuplicateClasses(ImmutableCollection<String> jars) { 1068 final HashMultimap<String, String> allClasses = HashMultimap.create(); 1069 Multimaps.invertFrom(Multimaps.filterKeys(sJarsToClasses, jars::contains), allClasses); 1070 return Multimaps.filterKeys(allClasses, key -> allClasses.get(key).size() > 1); 1071 } 1072 jarsInSameApex(Collection<String> jars)1073 private boolean jarsInSameApex(Collection<String> jars) { 1074 return jars.stream() 1075 .map(path -> apexForJar(path).orElse(path)) 1076 .distinct() 1077 .count() <= 1; 1078 } 1079 apexForJar(String jar)1080 private Optional<String> apexForJar(String jar) { 1081 Matcher m = APEX_JAR_PATTERN.matcher(jar); 1082 if (!m.matches()) { 1083 return Optional.empty(); 1084 } 1085 return Optional.of(m.group("apexName")); 1086 } 1087 doesFileExist(String path, ITestDevice device)1088 private static boolean doesFileExist(String path, ITestDevice device) { 1089 assertThat(path).isNotNull(); 1090 try { 1091 return device.doesFileExist(path); 1092 } catch (DeviceNotAvailableException e) { 1093 throw new RuntimeException("Could not check whether " + path + " exists on device", e); 1094 } 1095 } 1096 1097 /** 1098 * Get the name of a shared library. 1099 * 1100 * @return the shared library name or the jar's path if it's not a shared library. 1101 */ getSharedLibraryNameOrPath(String jar)1102 private String getSharedLibraryNameOrPath(String jar) { 1103 return sSharedLibs.stream() 1104 .filter(sharedLib -> sharedLib.paths.contains(jar)) 1105 .map(sharedLib -> sharedLib.name) 1106 .findFirst().orElse(jar); 1107 } 1108 1109 /** 1110 * Check whether a list of jars are all different versions of the same library. 1111 */ isSameLibrary(Collection<String> jars)1112 private boolean isSameLibrary(Collection<String> jars) { 1113 return jars.stream() 1114 .map(this::getSharedLibraryNameOrPath) 1115 .distinct() 1116 .count() == 1; 1117 } 1118 hasFeature(String featureName)1119 private boolean hasFeature(String featureName) throws DeviceNotAvailableException { 1120 return getDevice().executeShellCommand("pm list features").contains(featureName); 1121 } 1122 } 1123