Read services from META-INF/services/

Bug: 124181030
Change-Id: Icc929973524669ed388991d582b5d54f01861182
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 4f472c6..77968e6 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppServices;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.AppliedGraphLens;
 import com.android.tools.r8.graph.DexApplication;
@@ -263,6 +264,8 @@
       AppView<AppInfoWithSubtyping> appView =
           new AppView<>(
               new AppInfoWithSubtyping(application), GraphLense.getIdentityLense(), options);
+      appView.setAppServices(AppServices.builder(application).build());
+
       List<ProguardConfigurationRule> synthesizedProguardRules = new ArrayList<>();
       RootSet rootSet;
       String proguardSeedsData = null;
diff --git a/src/main/java/com/android/tools/r8/graph/AppServices.java b/src/main/java/com/android/tools/r8/graph/AppServices.java
new file mode 100644
index 0000000..cfa5898
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/AppServices.java
@@ -0,0 +1,108 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.DataDirectoryResource;
+import com.android.tools.r8.DataEntryResource;
+import com.android.tools.r8.DataResourceProvider;
+import com.android.tools.r8.DataResourceProvider.Visitor;
+import com.android.tools.r8.ProgramResourceProvider;
+import com.android.tools.r8.ResourceException;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.io.ByteStreams;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/** A description of the services and their implementations found in META-INF/services/. */
+public class AppServices {
+
+  private final Map<DexType, Set<DexType>> services;
+
+  private AppServices(Map<DexType, Set<DexType>> services) {
+    this.services = services;
+  }
+
+  public static Builder builder(DexApplication application) {
+    return new Builder(application);
+  }
+
+  public static class Builder {
+
+    private static final String SERVICE_DIRECTORY_NAME = "META-INF/services/";
+
+    private final DexApplication app;
+    private final Map<DexType, Set<DexType>> services = new IdentityHashMap<>();
+
+    private Builder(DexApplication app) {
+      this.app = app;
+    }
+
+    public AppServices build() {
+      for (ProgramResourceProvider programResourceProvider : app.programResourceProviders) {
+        DataResourceProvider dataResourceProvider =
+            programResourceProvider.getDataResourceProvider();
+        if (dataResourceProvider != null) {
+          readServices(dataResourceProvider);
+        }
+      }
+      return new AppServices(services);
+    }
+
+    private void readServices(DataResourceProvider dataResourceProvider) {
+      try {
+        dataResourceProvider.accept(new DataResourceProviderVisitor());
+      } catch (ResourceException e) {
+        throw new CompilationError(e.getMessage(), e);
+      }
+    }
+
+    private class DataResourceProviderVisitor implements Visitor {
+
+      @Override
+      public void visit(DataDirectoryResource directory) {
+        // Ignore.
+      }
+
+      @Override
+      public void visit(DataEntryResource file) {
+        try {
+          String name = file.getName();
+          if (name.startsWith(SERVICE_DIRECTORY_NAME)) {
+            String serviceName = name.substring(SERVICE_DIRECTORY_NAME.length());
+            if (DescriptorUtils.isValidJavaType(serviceName)) {
+              String serviceDescriptor = DescriptorUtils.javaTypeToDescriptor(serviceName);
+              DexType serviceType = app.dexItemFactory.createType(serviceDescriptor);
+              byte[] bytes = ByteStreams.toByteArray(file.getByteStream());
+              String contents = new String(bytes, Charset.defaultCharset());
+              services.put(serviceType, readServiceImplementationsForService(contents));
+            }
+          }
+        } catch (IOException | ResourceException e) {
+          throw new CompilationError(e.getMessage(), e);
+        }
+      }
+
+      private Set<DexType> readServiceImplementationsForService(String contents) {
+        if (contents != null) {
+          return Arrays.stream(contents.split(System.lineSeparator()))
+              .map(String::trim)
+              .filter(line -> !line.isEmpty())
+              .filter(DescriptorUtils::isValidJavaType)
+              .map(DescriptorUtils::javaTypeToDescriptor)
+              .map(app.dexItemFactory::createType)
+              .collect(Collectors.toSet());
+        }
+        return ImmutableSet.of();
+      }
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index e232cb9..084ad80 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -11,6 +11,7 @@
 public class AppView<T extends AppInfo> {
 
   private T appInfo;
+  private AppServices appServices;
   private final DexItemFactory dexItemFactory;
   private GraphLense graphLense;
   private final InternalOptions options;
@@ -31,6 +32,14 @@
     this.appInfo = appInfo;
   }
 
+  public AppServices appServices() {
+    return appServices;
+  }
+
+  public void setAppServices(AppServices appServices) {
+    this.appServices = appServices;
+  }
+
   public DexItemFactory dexItemFactory() {
     return dexItemFactory;
   }
@@ -86,6 +95,16 @@
     }
 
     @Override
+    public AppServices appServices() {
+      return AppView.this.appServices();
+    }
+
+    @Override
+    public void setAppServices(AppServices appServices) {
+      AppView.this.setAppServices(appServices);
+    }
+
+    @Override
     public DexItemFactory dexItemFactory() {
       return AppView.this.dexItemFactory();
     }