1 /* 2 * Copyright (C) 2021 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 package com.android.media.audiotestharness.server; 17 18 import com.android.media.audiotestharness.proto.AudioTestHarnessGrpc; 19 import com.android.media.audiotestharness.server.config.SharedHostConfiguration; 20 21 import com.google.common.base.Preconditions; 22 import com.google.inject.ConfigurationException; 23 import com.google.inject.Injector; 24 25 import io.grpc.Server; 26 import io.grpc.ServerBuilder; 27 28 import java.io.IOException; 29 import java.util.concurrent.Executor; 30 import java.util.concurrent.ExecutorService; 31 import java.util.logging.Level; 32 import java.util.logging.Logger; 33 34 /** 35 * gRPC Server to the Audio Test Harness Infrastructure. 36 * 37 * <p>This class is designed in such a way that the spin up and tear down of the gRPC server, 38 * including all dependencies are abstracted away from any users of Audio Test Harness. It is 39 * possible to use the service implementation directly and customize the deployment of the service, 40 * however, this is meant as an easy alternative for users who just want a working system and don't 41 * care how it works under the hood. 42 * 43 * <p>This class should not be subclassed or extended, however is left non-final for testing 44 * purposes. 45 */ 46 public class AudioTestHarnessGrpcServer implements AutoCloseable { 47 48 private static final Logger LOGGER = 49 Logger.getLogger(AudioTestHarnessGrpcServer.class.getName()); 50 51 /** The port on which the gRPC Server should be executed on. */ 52 private final int mPort; 53 54 /** 55 * {@link Executor} used for background task execution. These tasks can be the handling of gRPC 56 * calls or other background tasks in the system such as the publishing of audio sample data by 57 * a {@link com.android.media.audiotestharness.server.core.AudioCapturer}. 58 */ 59 private final ExecutorService mExecutor; 60 61 /** {@link Injector} used for dependency management and injection. */ 62 private final Injector mInjector; 63 64 /** Underlying {@link Server} that manages gRPC calls. */ 65 private Server mServer; 66 AudioTestHarnessGrpcServer(int port, ExecutorService executor, Injector injector)67 AudioTestHarnessGrpcServer(int port, ExecutorService executor, Injector injector) { 68 LOGGER.finest(String.format("new AudioTestHarnessGrpcServer on Port (%d)", port)); 69 70 this.mPort = port; 71 this.mExecutor = executor; 72 this.mInjector = injector; 73 } 74 main(String[] args)75 public static void main(String[] args) { 76 try (AudioTestHarnessGrpcServer server = 77 AudioTestHarnessGrpcServerFactory.createFactory() 78 .createOnTestingPort(SharedHostConfiguration.getDefault())) { 79 server.open(); 80 81 // Ensure that resources are cleanly stopped upon JVM shutdown. 82 Runtime.getRuntime().addShutdownHook(new Thread(server::close)); 83 84 // Wait for server execution to complete. So that CTRL + C or some other mechanism must 85 // be used to kill the server. 86 server.mServer.awaitTermination(); 87 88 } catch (Exception e) { 89 LOGGER.log(Level.SEVERE, "Unable to start the Audio Test Harness gRPC Server.", e); 90 } 91 } 92 93 /** 94 * Starts the Audio Test Harness gRPC Server listening on {@link #mPort}. 95 * 96 * @throws IOException if any errors occur while provisioning or starting the server. 97 */ open()98 public void open() throws IOException { 99 Preconditions.checkState(mServer == null, "Server has already been opened."); 100 101 ServerBuilder<?> serverBuilder = ServerBuilder.forPort(mPort).executor(mExecutor); 102 103 try { 104 serverBuilder.addService( 105 mInjector.getInstance(AudioTestHarnessGrpc.AudioTestHarnessImplBase.class)); 106 } catch (ConfigurationException ce) { 107 throw new IOException( 108 "Unable to create new Audio Test Harness gRPC Server, failed to resolve" 109 + " required dependency.", 110 ce); 111 } 112 113 mServer = serverBuilder.build(); 114 LOGGER.fine("Successfully created Audio Test Harness gRPC Server."); 115 116 mServer.start(); 117 LOGGER.info( 118 String.format( 119 "Started Audio Test Harness gRPC Server Listening on Port %d", mPort)); 120 } 121 122 /** 123 * Stops the Audio Test Harness gRPC Server immediately and closes any underlying resources 124 * being used by the server. 125 */ 126 @Override close()127 public void close() { 128 LOGGER.info("Stopping Audio Test Harness gRPC Server"); 129 if (mServer != null) { 130 mServer.shutdownNow(); 131 mServer = null; 132 } else { 133 LOGGER.warning( 134 "mServer is null indicating that the Audio Test Harness gRPC Server was never" 135 + " started."); 136 } 137 } 138 getPort()139 public int getPort() { 140 return mPort; 141 } 142 } 143