// Copyright 2020 Google LLC. All rights reserved. package com.google.video.widevine.jts.providers; import com.google.chrome.widevine.contentpartners.v1beta1.PublishedDevices; import com.google.chrome.widevine.contentpartners.v1beta1.PublishedDevicesRequest; import com.google.chrome.widevine.contentpartners.v1beta1.PublishedDevicesServiceGrpc; import com.google.inject.Inject; import com.google.protobuf.TextFormat; import com.google.video.widevine.jts.httpclient.Credentials; import com.google.video.widevine.jts.interfaces.DeviceCertificate; import com.google.video.widevine.sdk.wvpl.WvPLBaseEnvironment; import com.google.video.widevine.sdk.wvpl.WvPLStatusException; import io.grpc.ManagedChannel; import io.grpc.Metadata; import io.grpc.netty.NettyChannelBuilder; import io.grpc.stub.MetadataUtils; import java.io.IOException; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; /** * Provides the latest {@code PublishedDevices} data from the Widevine Published Devices Service. * * This implementation uses the Widevine Published Devices API, and support a gRPC method for * retrieving PublishedDevices with an embedded Published Devices list. */ public class PublishedDevicesProvider implements DeviceCertificate { private static final Logger logger = Logger.getLogger(PublishedDevicesProvider.class.getName()); private WvPLBaseEnvironment environment = null; private String apiServicePath = null; private Credentials credentials = null; /** * PublishedDevicesProvider constructor. * * @param environment A WvPLBaseEnvironment object initialized with a Service Certificate. * @param serviceAccountPath Path to a GCP Service Account json file, used in OAUTH. * @param apiServicePath Path to a Widevine Published Devices API service. * @throws IOException upon failure creating OAUTH credentials. */ @Inject public PublishedDevicesProvider( WvPLBaseEnvironment environment, String serviceAccountPath, String apiServicePath) throws IOException { this.environment = environment; this.apiServicePath = apiServicePath; credentials = new Credentials(serviceAccountPath); } /** * Get the latest {@code PublishedDevices} from the Widevine Published Devices Service. * * @return The latest {@code PublishedDevices} data. * @throws InterruptedException upon RPC failure. * @throws WvPLStatusException upon WvPLBaseEnvironment errors. */ @Override public PublishedDevices getPublishedDevices() throws InterruptedException, WvPLStatusException{ ManagedChannel channel = null; PublishedDevices devicesResponse = null; logger.log(Level.INFO, "Getting PublishedDevices..."); try { channel = createRpcChannel(apiServicePath); Metadata metadata = new Metadata(); String token = "Bearer " + credentials.getAccessToken(); metadata.put(Metadata.Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER), token); PublishedDevicesServiceGrpc.PublishedDevicesServiceBlockingStub blockingStub = PublishedDevicesServiceGrpc.newBlockingStub(channel) .withInterceptors(MetadataUtils.newAttachHeadersInterceptor(metadata)); devicesResponse = blockingStub.getPublishedDevices(PublishedDevicesRequest.parseFrom( environment.generateDeviceStatusListRequest())); logger.log(Level.INFO, "GRPC Call to PublishedDevicesService.GetSignedList returned:\n %s" + TextFormat.printer().printToString(devicesResponse)); } catch (IOException e) { logger.log(Level.INFO, "IOException encountered trying to retrieve the signed list: " + e); } finally { if (channel != null) { channel.shutdown(); channel.awaitTermination(1, TimeUnit.SECONDS); } } return devicesResponse; } private static ManagedChannel createRpcChannel(String host) { return NettyChannelBuilder.forTarget(host).build(); } }