Add resource shrinker sources to R8 repo

This is a verbatim copy for now since this will allow us to easily
merge in either direction - downside is that this does not pass our formatter.

I have added 8.2.20-dev to third_party/r8 to allow us to compile this standalone, this is not used when compiled into r8 (in a follow up). We should be able to refactor the setup to eventually not need this hack.

Add build setup in new gradle build files

Bug: 287398085
Change-Id: Ib9ab46acb67dc4d713e91b82f27b30ef67fb065a
diff --git a/src/resourceshrinker/java/com/android/build/shrinker/ResourceShrinkerModel.java b/src/resourceshrinker/java/com/android/build/shrinker/ResourceShrinkerModel.java
new file mode 100644
index 0000000..ce1b39f
--- /dev/null
+++ b/src/resourceshrinker/java/com/android/build/shrinker/ResourceShrinkerModel.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.shrinker;
+
+import com.android.aapt.Resources.ResourceTable;
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.build.shrinker.obfuscation.ObfuscatedClasses;
+import com.android.ide.common.resources.usage.ResourceStore;
+import com.android.ide.common.resources.usage.ResourceUsageModel.Resource;
+import com.android.resources.ResourceType;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Represents resource shrinker state that is shared between shrinker stages ResourcesGatherer,
+ * ObfuscationMappingsRecorder, ResourceUsageRecorder, ResourcesGraphBuilder and each stage
+ * contributes an information via changing its state.
+ */
+public class ResourceShrinkerModel {
+
+    private final ShrinkerDebugReporter debugReporter;
+
+    private final ResourceStore resourceStore;
+    private ObfuscatedClasses obfuscatedClasses = ObfuscatedClasses.NO_OBFUSCATION;
+
+    private final Set<String> strings = Sets.newHashSetWithExpectedSize(300);
+
+    private final Map<String, ResourceTable> resourceTableCache = Maps.newHashMap();
+
+    private boolean foundGetIdentifier = false;
+    private boolean foundWebContent = false;
+
+    public ResourceShrinkerModel(
+            ShrinkerDebugReporter debugReporter, boolean supportMultipackages) {
+        this.debugReporter = debugReporter;
+        resourceStore = new ResourceStore(supportMultipackages);
+    }
+
+    @NonNull
+    public ShrinkerDebugReporter getDebugReporter() {
+        return debugReporter;
+    }
+
+    @NonNull
+    public ResourceStore getResourceStore() {
+        return resourceStore;
+    }
+
+    @NonNull
+    public ObfuscatedClasses getObfuscatedClasses() {
+        return obfuscatedClasses;
+    }
+
+    /** Reports recorded mappings from obfuscated classes to original classes */
+    public void setObfuscatedClasses(@NonNull ObfuscatedClasses obfuscatedClasses) {
+        this.obfuscatedClasses = obfuscatedClasses;
+    }
+
+    /** Adds a new gathered resource to model. */
+    @NonNull
+    public Resource addResource(
+            @NonNull ResourceType type,
+            @Nullable String packageName,
+            @NonNull String name,
+            @Nullable String value) {
+        int intValue = -1;
+        try {
+            intValue = value != null ? Integer.decode(value) : -1;
+        } catch (NumberFormatException e) {
+            // ignore
+        }
+        return addResource(type, packageName, name, intValue);
+    }
+
+    /** Adds a new gathered resource to model. */
+    @NonNull
+    public Resource addResource(
+            @NonNull ResourceType type,
+            @Nullable String packageName,
+            @NonNull String name,
+            int value) {
+        return resourceStore.addResource(new Resource(packageName, type, name, value));
+    }
+
+    /** Adds string constant found in code to strings pool. */
+    public void addStringConstant(@NonNull String string) {
+        strings.add(string);
+    }
+
+    /** Returns is reference to {@code Resources#getIdentifier} is found in code. */
+    public boolean isFoundGetIdentifier() {
+        return foundGetIdentifier;
+    }
+
+    /** Reports that reference to {@code Resources#getIdentifier} is found in code. */
+    public void setFoundGetIdentifier(boolean foundGetIdentifier) {
+        this.foundGetIdentifier = foundGetIdentifier;
+    }
+
+    /** Returns is web content is found in code. */
+    public boolean isFoundWebContent() {
+        return foundWebContent;
+    }
+
+    /** Reports that reference to web content is found in code. */
+    public void setFoundWebContent(boolean foundWebContent) {
+        this.foundWebContent = foundWebContent;
+    }
+
+    /** Returns all string constants gathered from compiled classes. */
+    public Set<String> getStrings() {
+        return strings;
+    }
+
+    /**
+     * Mark resources that match string constants as reachable in case invocation of
+     * {@code Resources#getIdentifier} or web content is found in code and safe mode in enabled.
+     */
+    public void keepPossiblyReferencedResources() {
+        if (strings.isEmpty()
+                || !resourceStore.getSafeMode()
+                || (!foundGetIdentifier && !foundWebContent)) {
+            // No calls to android.content.res.Resources#getIdentifier; or user specifically asked
+            // for us not to guess resources to keep
+            return;
+        }
+
+        debugReporter.debug(() -> ""
+                        + "android.content.res.Resources#getIdentifier present: " + foundGetIdentifier + "\n"
+                        + "Web content present: " + foundWebContent + "\n"
+                        + "Referenced Strings:\n"
+                        + strings.stream()
+                            .map(s -> s.trim().replace("\n", "\\n"))
+                            .filter(s -> !s.isEmpty())
+                            .map(s -> s.length() > 40 ? s.substring(0, 37) + "..." : s)
+                            .collect(Collectors.joining("\n"))
+        );
+
+        new PossibleResourcesMarker(debugReporter, resourceStore, strings, foundWebContent)
+                .markPossibleResourcesReachable();
+    }
+
+    /**
+     * Reads resource table from specified path and stores it in cache to be able to reuse it if
+     * the same resource table is requested by another unit.
+     */
+    public ResourceTable readResourceTable(Path resourceTablePath) {
+        return resourceTableCache.computeIfAbsent(resourceTablePath.toString(), (path) -> {
+            try {
+                return ResourceTable.parseFrom(Files.readAllBytes(resourceTablePath));
+            } catch (IOException e) {
+                throw new UncheckedIOException(e);
+            }
+        });
+    }
+}