• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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