Commit 5873e146 authored by mlerman's avatar mlerman Committed by Commit bot

Revert of Registration of DevTools bridge in GCD. (patchset #4 id:80001 of...

Revert of Registration of DevTools bridge in GCD. (patchset #4 id:80001 of https://codereview.chromium.org/677843006/)

Reason for revert:
Only CL that landed before http://build.chromium.org/p/chromium.mac/waterfall?builder=Mac%20Builder broke. Going to revert this CL to try and get the builder working again.

Original issue's description:
> Registration of DevTools bridge in GCD.
>
> Code for registering and unregistering instance of DevTools bridge in GCD.
> GCDRegistrationFragment is able to perform registration including:
> 1. Asking user for desired account to register.
> 2. Requesting OAuth permissions for GCD (may show confirmation activity to the user).
> 3. Registration in Cloud Messages if needed.
>
> It also shows toasts with registration/unregistration results.
>
> GCDRegistrationFragment is absstract and has no views. Testing implementation
> shows button "Registed in GCD/Unregister (registered for ...)". Registration is not
> persistent yet.
>
> BUG=383418
> TEST=New button in main activity of DevToolsBridgeTest.apk: register/unregister.
>
> Committed: https://crrev.com/a2da02bd99d78de549b617510551ec5e1f7073d9
> Cr-Commit-Position: refs/heads/master@{#302251}

TBR=mnaganov@chromium.org,serya@chromium.org
NOTREECHECKS=true
NOTRY=true
BUG=383418

Review URL: https://codereview.chromium.org/693153003

Cr-Commit-Position: refs/heads/master@{#302254}
parent 525cddf6
......@@ -12,7 +12,6 @@
},
'includes': [ '../build/java.gypi' ],
'dependencies': [
'../third_party/android_tools/android_tools.gyp:android_gcm',
'../third_party/libjingle/libjingle.gyp:libjingle_peerconnection_javalib',
],
},
......
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.components.devtools_bridge.apiary;
import android.net.http.AndroidHttpClient;
/**
* Factory for creating clients for external APIs.
*/
public abstract class ApiaryClientFactory {
private static final String USER_AGENT = "DevTools bridge";
public static final String OAUTH_SCOPE = "https://www.googleapis.com/auth/clouddevices";
private final AndroidHttpClient mHttpClient = AndroidHttpClient.newInstance(USER_AGENT);
/**
* Creates a new GCD client with auth token.
*/
public GCDClient newGCDClient(String oAuthToken) {
return new GCDClient(mHttpClient, getAPIKey(), oAuthToken);
}
/**
* Creates a new anonymous client. GCD requires client been not authenticated by user or
* device credentials for finalizing registration.
*/
public GCDClient newAnonymousGCDClient() {
return new GCDClient(mHttpClient, getAPIKey());
}
public OAuthClient newOAuthClient() {
return new OAuthClient(
mHttpClient, OAUTH_SCOPE, getOAuthClientId(), getOAuthClientSecret());
}
public BlockingGCMRegistrar newGCMRegistrar() {
return new BlockingGCMRegistrar() {
@Override
protected String[] getSenderIds() {
return getGCMSenderIds();
}
};
}
public void close() {
mHttpClient.close();
}
public abstract String getAPIKey();
public abstract String getOAuthClientId();
public abstract String getOAuthClientSecret();
public abstract String[] getGCMSenderIds();
}
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.components.devtools_bridge.apiary;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.util.Log;
import com.google.android.gcm.GCMConstants;
import com.google.android.gcm.GCMRegistrar;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
/**
* Helps using GCDRegistrar in blocking manner. If the app has not registered in GCM
* it sends registration request and waits for an intent with registration ID.
* Waiting may be interrupted. Must not be used on UI (or Context's main) looper.
*/
public abstract class BlockingGCMRegistrar {
private static final String TAG = "BlockingGCMRegistrar";
public String blockingGetRegistrationId(Context context)
throws InterruptedException, IOException {
assert context != null;
Receiver receiver = new Receiver();
receiver.register(context);
try {
String result = GCMRegistrar.getRegistrationId(context);
if (result != null && !result.isEmpty()) return result;
GCMRegistrar.register(context, getSenderIds());
return receiver.awaitRegistrationId();
} finally {
receiver.unregister(context);
}
}
protected abstract String[] getSenderIds();
private static class Receiver extends BroadcastReceiver {
private final CountDownLatch mDone = new CountDownLatch(1);
private String mRegistrationId;
private String mError;
public void register(Context context) {
IntentFilter filter = new IntentFilter();
filter.addCategory(context.getPackageName());
filter.addAction(GCMConstants.INTENT_FROM_GCM_REGISTRATION_CALLBACK);
context.registerReceiver(this, filter);
}
public void unregister(Context context) {
context.unregisterReceiver(this);
}
public String awaitRegistrationId() throws InterruptedException, IOException {
mDone.await();
if (mRegistrationId != null) {
return mRegistrationId;
}
throw new IOException(mError);
}
@Override
public void onReceive(Context context, Intent intent) {
assert intent.getAction().equals(GCMConstants.INTENT_FROM_GCM_REGISTRATION_CALLBACK);
mRegistrationId = intent.getStringExtra(GCMConstants.EXTRA_REGISTRATION_ID);
mError = intent.getStringExtra(GCMConstants.EXTRA_ERROR);
if (mRegistrationId != null || mError != null) {
mDone.countDown();
} else {
Log.e(TAG, "Unexpected intent: " + intent);
}
}
}
}
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.components.devtools_bridge.apiary;
import android.util.JsonReader;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.HttpResponseException;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.StringEntity;
import org.chromium.components.devtools_bridge.gcd.InstanceCredential;
import org.chromium.components.devtools_bridge.gcd.InstanceDescription;
import org.chromium.components.devtools_bridge.gcd.MessageReader;
import org.chromium.components.devtools_bridge.gcd.MessageWriter;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
/**
* Client for accessing GCD API.
*/
public class GCDClient {
private static final String API_BASE = "https://www.googleapis.com/clouddevices/v1";
public static final String ENCODING = "UTF-8";
protected static final String CONTENT_TYPE = "application/json; charset=" + ENCODING;
private final HttpClient mHttpClient;
private final String mAPIKey;
private final String mOAuthToken;
GCDClient(HttpClient httpClient, String apiKey, String oAuthToken) {
mHttpClient = httpClient;
mAPIKey = apiKey;
mOAuthToken = oAuthToken;
}
GCDClient(HttpClient httpClient, String apiKey) {
this(httpClient, apiKey, null);
}
/**
* Creation of a registration ticket is the first step in instance registration. Client must
* have user credentials. If the ticket has been registered it will be associated with the
* user. Next step is registration ticket patching.
*/
public String createRegistrationTicket() throws IOException {
assert mOAuthToken != null;
return mHttpClient.execute(
newHttpPost("/registrationTickets", "{\"userEmail\":\"me\"}"),
new JsonResponseHandler<String>() {
@Override
public String readResponse(JsonReader reader) throws IOException {
return new MessageReader(reader).readTicketId();
}
});
}
/**
* Patching registration ticket. GCD gets device definition including commands metadata,
* GCM channel description and user-visible instance name.
*/
public void patchRegistrationTicket(String ticketId, InstanceDescription description)
throws IOException {
String content = new MessageWriter().writeTicketPatch(description).close().toString();
mHttpClient.execute(
newHttpPatch("/registrationTickets/" + ticketId, content),
new EmptyResponseHandler());
}
/**
* Finalizing registration. Client must be anonymous (GCD requirement). GCD provides
* instance credentials needed for handling commands.
*/
public InstanceCredential finalizeRegistration(String ticketId) throws IOException {
return mHttpClient.execute(
newHttpPost("/registrationTickets/" + ticketId + "/finalize", ""),
new JsonResponseHandler<InstanceCredential>() {
@Override
public InstanceCredential readResponse(JsonReader reader) throws IOException {
return new MessageReader(reader).readInstanceCredential();
}
});
}
/**
* Deletes registered instance (unregisters). If client has instance credentials then
* instanceId must be it's own ID. If client has user credentials then instance must belong
* to the user.
*/
public void deleteInstance(String instanceId) throws IOException {
mHttpClient.execute(
newHttpDelete("/devices/" + instanceId),
new EmptyResponseHandler());
}
private HttpPost newHttpPost(String path, String content) throws UnsupportedEncodingException {
HttpPost request = new HttpPost(buildUrl(path));
setContent(request, content);
initializeRequest(request);
return request;
}
private HttpPatch newHttpPatch(String path, String content)
throws UnsupportedEncodingException {
HttpPatch request = new HttpPatch(buildUrl(path));
setContent(request, content);
initializeRequest(request);
return request;
}
private HttpDelete newHttpDelete(String path) {
HttpDelete request = new HttpDelete(buildUrl(path));
initializeRequest(request);
return request;
}
private String buildUrl(String path) {
return API_BASE + path + "?key=" + mAPIKey;
}
private void setContent(HttpEntityEnclosingRequestBase request, String content)
throws UnsupportedEncodingException {
request.setEntity(new StringEntity(content, ENCODING));
request.addHeader("Content-Type", CONTENT_TYPE);
}
private void initializeRequest(HttpRequestBase request) {
if (mOAuthToken != null) {
request.addHeader("Authorization", "Bearer " + mOAuthToken);
}
}
private static final class HttpPatch extends HttpEntityEnclosingRequestBase {
public HttpPatch(String uri) {
setURI(URI.create(uri));
}
public String getMethod() {
return "PATCH";
}
}
private static class EmptyResponseHandler implements ResponseHandler<Void> {
@Override
public Void handleResponse(HttpResponse response) throws HttpResponseException {
JsonResponseHandler.checkStatus(response);
return null;
}
}
}
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.components.devtools_bridge.apiary;
import android.util.JsonReader;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpResponseException;
import org.apache.http.client.ResponseHandler;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* Base class for a ResponseHandler that reads response with JsonReader. Like BasicResponseHandler
* throws HttpResponseException if response code >= 300.
*
* It catchs JsonReader's runtime exception (IllegalStateException and IllegalArgumentException)
* and wraps them into ResponseFormatException.
*/
abstract class JsonResponseHandler<T> implements ResponseHandler<T> {
public static void checkStatus(HttpResponse response) throws HttpResponseException {
StatusLine statusLine = response.getStatusLine();
if (response.getStatusLine().getStatusCode() >= 300) {
throw new HttpResponseException(
statusLine.getStatusCode(), statusLine.getReasonPhrase());
}
}
@Override
public final T handleResponse(HttpResponse response)
throws IOException, ClientProtocolException {
checkStatus(response);
HttpEntity entity = response.getEntity();
if (entity == null) {
throw new ClientProtocolException("Missing content");
}
JsonReader reader = new JsonReader(new InputStreamReader(entity.getContent()));
try {
T result = readResponse(reader);
reader.close();
if (result == null) {
throw new ClientProtocolException("Missing result");
}
return result;
} catch (IllegalStateException e) {
throw new ResponseFormatException(e);
} catch (IllegalArgumentException e) {
throw new ResponseFormatException(e);
}
}
public abstract T readResponse(JsonReader reader)
throws IOException, ResponseFormatException;
public static class ResponseFormatException extends ClientProtocolException {
public ResponseFormatException(RuntimeException readerException) {
super(readerException);
}
public ResponseFormatException() {}
}
}
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.components.devtools_bridge.apiary;
import android.util.JsonReader;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
/**
* Google authentication client. Fetches a pair of refresh/access tokens for a
* secret received while registering the instance in GCD.
*/
public class OAuthClient {
public static final String API_BASE = "https://accounts.google.com/o/oauth2";
public static final String ENCODING = "UTF-8";
public static final String CONTENT_TYPE = "application/x-www-form-urlencoded";
private final HttpClient mHttpClient;
private final String mScope;
private final String mClientId;
private final String mClientSecret;
OAuthClient(HttpClient httpClient, String scope, String clientId, String clientSecret) {
assert httpClient != null;
assert scope != null;
assert clientId != null;
assert clientSecret != null;
mHttpClient = httpClient;
mScope = scope;
mClientId = clientId;
mClientSecret = clientSecret;
}
public OAuthResult authenticate(String secret) throws IOException {
final long startTimeMs = System.currentTimeMillis();
String content =
"client_id=" + urlEncode(mClientId)
+ "&client_secret=" + urlEncode(mClientSecret)
+ "&scope=" + urlEncode(mScope)
+ "&code=" + urlEncode(secret)
+ "&redirect_uri=oob"
+ "&grant_type=authorization_code";
return mHttpClient.execute(
newHttpPost("/token", content),
new JsonResponseHandler<OAuthResult>() {
@Override
public OAuthResult readResponse(JsonReader reader) throws IOException {
return readResponse(reader, startTimeMs);
}
});
}
private HttpPost newHttpPost(String path, String content) throws UnsupportedEncodingException {
HttpPost request = new HttpPost(API_BASE + "/token");
request.setEntity(new StringEntity(content, ENCODING));
request.addHeader("Content-Type", CONTENT_TYPE);
return request;
}
private static String urlEncode(String value) {
try {
return URLEncoder.encode(value, ENCODING);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
static OAuthResult readResponse(JsonReader reader, long startTimeMs) throws IOException {
String refreshToken = null;
String accessToken = null;
long expiresInS = 0; // In seconds.
reader.beginObject();
while (reader.hasNext()) {
String name = reader.nextName();
if (name.equals("refresh_token")) {
refreshToken = reader.nextString();
} else if (name.equals("access_token")) {
accessToken = reader.nextString();
} else if (name.equals("expires_in")) {
expiresInS = reader.nextLong();
} else {
reader.skipValue();
}
}
reader.endObject();
return OAuthResult.create(
refreshToken, accessToken, startTimeMs + expiresInS * 1000);
}
}
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.components.devtools_bridge.apiary;
/**
* Pair of refresh/access tokens fetched with OAuthClient.
*/
public class OAuthResult {
public String refreshToken;
public String accessToken;
public final long expirationTimeMs; // In milliseconds.
private OAuthResult(String refreshToken, String accessToken, long expirationTimeMs) {
assert refreshToken != null;
this.refreshToken = refreshToken;
this.accessToken = accessToken;
this.expirationTimeMs = expirationTimeMs;
}
public static OAuthResult create(
String refreshToken, String accessToken, long expirationTimeMs) {
return refreshToken != null
? new OAuthResult(refreshToken, accessToken, expirationTimeMs) : null;
}
}
......@@ -13,14 +13,14 @@ import java.util.Map;
*/
public abstract class CommandDefinition {
private final String mName;
private final List<ParamDefinition<?>> mInParams;
private final List<ParamDefinition> mInParams;
public CommandDefinition(String name, List<ParamDefinition<?>> inParams) {
public CommandDefinition(String name, List<ParamDefinition> inParams) {
mName = name;
mInParams = inParams;
}
public Iterable<ParamDefinition<?>> inParams() {
public Iterable<ParamDefinition> inParams() {
return mInParams;
}
......
......@@ -111,7 +111,7 @@ final class Commands {
}
@Override
protected ParamDefinition<String> resultDefinition() {
protected ParamDefinition resultDefinition() {
return PARAM_ANSWER;
}
}
......@@ -149,7 +149,7 @@ final class Commands {
}
@Override
protected ParamDefinition<List<String>> resultDefinition() {
protected ParamDefinition resultDefinition() {
return PARAM_SERVER_CANDIDATES;
}
}
......@@ -185,12 +185,12 @@ final class Commands {
}
@Override
protected ParamDefinition<String> resultDefinition() {
protected ParamDefinition resultDefinition() {
return PARAM_ANSWER;
}
}
private static List<ParamDefinition<?>> params(ParamDefinition<?>... values) {
private static List<ParamDefinition> params(ParamDefinition... values) {
return Collections.unmodifiableList(Arrays.asList(values));
}
}
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.components.devtools_bridge.gcd;
/**
* Information provided by GCD when instance has registered. Instance id
* can be used for:
* 1. Making sure incoming messages are addressed to the instance.
* 2. It needed when sending command results to GCD.
* 3. For device unregistration.
*
* The Secret supposed to be used to authenticate the instance with OAuthClient
* (it has no user credentials).
*/
public final class InstanceCredential {
public final String id;
public final String secret;
public InstanceCredential(String id, String secret) {
assert id != null;
assert secret != null;
this.id = id;
this.secret = secret;
}
}
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.components.devtools_bridge.gcd;
/**
* Information needed for registration in GCD.
* Instance secret will be bound to oAuthClientId.
* gcmChannelId will be used for delivering commands.
* displayName is a human readable name on the client side.
*/
public final class InstanceDescription {
public final String oAuthClientId;
public final String gcmChannelId;
public final String displayName;
private InstanceDescription(String oAuthClientId, String gcmChannelId, String displayName) {
assert oAuthClientId != null;
assert gcmChannelId != null;
assert displayName != null;
this.oAuthClientId = oAuthClientId;
this.gcmChannelId = gcmChannelId;
this.displayName = displayName;
}
/**
* Builder for InstanceDescription.
*/
public static final class Builder {
private String mOAuthClientId;
private String mGCMChannelId;
private String mDisplayName;
public Builder setOAuthClientId(String value) {
mOAuthClientId = value;
return this;
}
public Builder setGCMChannelId(String value) {
mGCMChannelId = value;
return this;
}
public Builder setDisplayName(String value) {
mDisplayName = value;
return this;
}
public InstanceDescription build() {
return new InstanceDescription(mOAuthClientId, mGCMChannelId, mDisplayName);
}
}
}
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.components.devtools_bridge.gcd;
import android.util.JsonReader;
import java.io.IOException;
/**
* Helper class for parsing JSON-encoded GCD messages (HTTP responses and GCM notifications) used
* in the DevTools bridge.
*/
public final class MessageReader {
private final JsonReader mReader;
public MessageReader(JsonReader reader) {
mReader = reader;
}
/**
* Reads id from a registration ticket.
*/
public String readTicketId() throws IOException {
return new TicketReader().readId();
}
/**
* Reads credentials from finalized registration ticket.
*/
public InstanceCredential readInstanceCredential() throws IOException {
return new TicketReader().readCredential();
}
private abstract class ObjectReader {
public final void readObject() throws IOException {
mReader.beginObject();
while (mReader.hasNext()) {
readItem(mReader.nextName());
}
mReader.endObject();
}
protected void readItem(String name) throws IOException {
mReader.skipValue();
}
}
private class TicketReader extends ObjectReader {
private String mId;
private String mDeviceId;
private String mDeviceSecret;
public String readId() throws IOException {
readObject();
if (mId == null) {
throw new IllegalArgumentException();
}
return mId;
}
public InstanceCredential readCredential() throws IOException {
readObject();
if (mDeviceId == null || mDeviceSecret == null) {
throw new IllegalArgumentException();
}
return new InstanceCredential(mDeviceId, mDeviceSecret);
}
@Override
protected void readItem(String name) throws IOException {
if (name.equals("id")) {
mId = mReader.nextString();
} else if (name.equals("deviceId")) {
mDeviceId = mReader.nextString();
} else if (name.equals("robotAccountAuthorizationCode")) {
mDeviceSecret = mReader.nextString();
} else {
super.readItem(name);
}
}
}
}
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.components.devtools_bridge.gcd;
import android.util.JsonWriter;
import org.chromium.components.devtools_bridge.commands.Command;
import org.chromium.components.devtools_bridge.commands.ParamDefinition;
import java.io.IOException;
import java.io.StringWriter;
/**
* Helper class for constructing GCD JSON messages (HTTP requests) used in the DevTools bridge.
*/
public final class MessageWriter {
private final StringWriter mStringWriter;
private final JsonWriter mWriter;
boolean mClosed = false;
public MessageWriter() {
mStringWriter = new StringWriter();
mWriter = new JsonWriter(mStringWriter);
}
public MessageWriter close() throws IOException {
assert !mClosed;
mWriter.close();
mClosed = true;
return this;
}
@Override
public String toString() {
assert mClosed;
return mStringWriter.toString();
}
/**
* Write body of registrationTicket PATCH request.
*/
public MessageWriter writeTicketPatch(InstanceDescription description) throws IOException {
mWriter.beginObject();
mWriter.name("deviceDraft");
writeDeviceDraft(description);
mWriter.name("oauthClientId").value(description.oAuthClientId);
mWriter.endObject();
return this;
}
private void writeDeviceDraft(InstanceDescription description) throws IOException {
mWriter.beginObject();
mWriter.name("deviceKind").value("vendor");
mWriter.name("displayName").value(description.displayName);
mWriter.name("systemName").value("Chrome DevTools Bridge");
mWriter.name("channel");
writeChannelDefinition(description);
mWriter.name("commandDefs");
writeCommandsDefinition();
mWriter.endObject();
}
private void writeChannelDefinition(InstanceDescription description) throws IOException {
mWriter.beginObject();
mWriter.name("supportedType").value("gcm");
mWriter.name("gcmRegistrationId").value(description.gcmChannelId);
mWriter.endObject();
}
private void writeCommandsDefinition() throws IOException {
mWriter.beginObject();
mWriter.name("base");
writeCommandsDefinitionBase();
mWriter.endObject();
}
private void writeCommandsDefinitionBase() throws IOException {
mWriter.beginObject();
for (Command.Type type : Command.Type.values()) {
mWriter.name(type.definition.shortName());
beginParameters();
for (ParamDefinition<?> param : type.definition.inParams()) {
writeParameter(param.name(), param.type());
}
endParameters();
}
mWriter.endObject();
}
private void beginParameters() throws IOException {
mWriter.beginObject();
mWriter.name("parameters");
mWriter.beginObject();
}
private void endParameters() throws IOException {
mWriter.endObject();
mWriter.endObject();
}
private void writeParameter(String name, String type) throws IOException {
mWriter.name(name);
mWriter.beginObject();
mWriter.name("type").value(type);
mWriter.endObject();
}
}
......@@ -30,8 +30,6 @@
<uses-permission android:name="android.permission.RUN_INSTRUMENTATION" />
<uses-permission android:name="android.permission.INJECT_EVENTS" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<!-- For manual testing with Chrome Shell -->
<uses-permission android:name="org.chromium.chrome.shell.permission.DEBUG" />
......
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.components.devtools_bridge.apiary;
import android.test.InstrumentationTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.JsonReader;
import junit.framework.Assert;
import org.apache.http.ProtocolVersion;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpResponseException;
import org.apache.http.entity.StringEntity;
import org.apache.http.message.BasicHttpResponse;
import java.io.IOException;
/**
* Tests for {@link JsonResponseHandler}.
*/
public class JsonResponseHandlerTest extends InstrumentationTestCase {
@SmallTest
public void testInvalidResponse() throws IOException {
try {
new JsonResponseHandler<String>() {
@Override
public String readResponse(JsonReader reader) {
return "";
}
}.handleResponse(newResponse(404, null));
Assert.fail();
} catch (HttpResponseException e) {
// Expected
}
}
@SmallTest
public void testIOException() {
try {
new JsonResponseHandler<String>() {
@Override
public String readResponse(JsonReader reader) throws IOException {
throw new IOException();
}
}.handleResponse(newResponse(200, ""));
Assert.fail();
} catch (IOException e) {
// Expected
}
}
@SmallTest
public void testStateException() throws IOException {
try {
new JsonResponseHandler<String>() {
@Override
public String readResponse(JsonReader reader) throws IOException {
reader.beginObject();
reader.endObject();
return "";
}
}.handleResponse(newResponse(200, "[]"));
Assert.fail();
} catch (JsonResponseHandler.ResponseFormatException e) {
// Expected
}
}
@SmallTest
public void testFormatException() throws IOException {
try {
new JsonResponseHandler<String>() {
@Override
public String readResponse(JsonReader reader) throws IOException {
reader.beginArray();
reader.nextLong();
reader.endArray();
return "";
}
}.handleResponse(newResponse(200, "[\"XXX\"]"));
Assert.fail();
} catch (JsonResponseHandler.ResponseFormatException e) {
// Expected
}
}
@SmallTest
public void testNullResultException() throws IOException {
try {
new JsonResponseHandler<String>() {
@Override
public String readResponse(JsonReader reader) throws IOException {
reader.beginArray();
reader.endArray();
return null;
}
}.handleResponse(newResponse(200, "[]"));
Assert.fail();
} catch (ClientProtocolException e) {
// Expected
}
}
@SmallTest
public void testSuccess() throws IOException {
String result = new JsonResponseHandler<String>() {
@Override
public String readResponse(JsonReader reader) throws IOException {
reader.beginArray();
reader.endArray();
return "OK";
}
}.handleResponse(newResponse(200, "[]"));
Assert.assertEquals("OK", result);
}
private BasicHttpResponse newResponse(int status, String content) {
BasicHttpResponse response = new BasicHttpResponse(
new ProtocolVersion("HTTP", 1, 1), status, "reason");
if (content != null) {
try {
response.setEntity(new StringEntity(content, "UTF-8"));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return response;
}
}
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.components.devtools_bridge.apiary;
import android.test.InstrumentationTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import junit.framework.Assert;
import org.chromium.components.devtools_bridge.util.TestSource;
/**
* Tests for {@link OAuthClient}.
*/
public class OAuthClientTest extends InstrumentationTestCase {
private static final String ACCESS_TOKEN =
"ya29.rgBgy64Y1MACXNmPDUpPGbwFuAec2NCCDJwaEp8DwLnV8RBk45p9RBqBfEQUYxL6OVB-oyktRqZj0w";
private static final String REFRESH_TOKEN =
"1/cWihsJmDMujYfhzBVTwgh4ukiFyiiRWLmFwTv4EigzU";
@SmallTest
public void testResponse() throws Exception {
TestSource source = new TestSource();
source.write()
.beginObject()
.name("access_token").value(ACCESS_TOKEN)
.name("token_type").value("Bearer")
.name("expires_in").value(3600) // seconds
.name("refresh_token").value(REFRESH_TOKEN)
.endObject()
.close();
OAuthResult result = OAuthClient.readResponse(source.read(), 1111);
Assert.assertEquals(ACCESS_TOKEN, result.accessToken);
Assert.assertEquals(REFRESH_TOKEN, result.refreshToken);
Assert.assertEquals(1111 + 3600000, result.expirationTimeMs);
}
}
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.components.devtools_bridge.gcd;
import android.test.InstrumentationTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import junit.framework.Assert;
import org.chromium.components.devtools_bridge.util.TestSource;
/**
* Tests for {@link MessageReaderTest}.
*/
public class MessageReaderTest extends InstrumentationTestCase {
private static final String DEVICE_ID = "4ac8a0f8-??????????????-192e2727710d";
private static final String ROBOT_ACCOUNT_EMAIL =
"2a3???????????????????????????87@clouddevices.gserviceaccount.com";
private static final String AUTHORIZATION_CODE =
"4/6V0jpup-????????????????????????????????????????????_e85kQI";
@SmallTest
public void testReadTicket() throws Exception {
TestSource source = new TestSource();
source.write().beginObject()
.name("kind").value("clouddevices#registrationTicket")
.name("id").value("p8hI4")
.name("deviceId").value(DEVICE_ID)
.name("creationTimeMs").value("1411029429794")
.name("expirationTimeMs").value("1411029669794")
.endObject().close();
String result = new MessageReader(source.read()).readTicketId();
Assert.assertEquals("p8hI4", result);
}
@SmallTest
public void testReadCredential() throws Exception {
TestSource source = new TestSource();
source.write().beginObject()
.name("kind").value("clouddevices#registrationTicket")
.name("id").value("p8hI4")
.name("deviceId").value(DEVICE_ID)
.name("userEmail").value("...@chromium.org")
.name("creationTimeMs").value("1411029429794")
.name("expirationTimeMs").value("1411029669794")
.name("robotAccountEmail").value(ROBOT_ACCOUNT_EMAIL)
.name("robotAccountAuthorizationCode").value(AUTHORIZATION_CODE)
.endObject().close();
InstanceCredential result = new MessageReader(source.read()).readInstanceCredential();
Assert.assertEquals(DEVICE_ID, result.id);
Assert.assertEquals(AUTHORIZATION_CODE, result.secret);
}
}
......@@ -6,26 +6,17 @@ package org.chromium.components.devtools_bridge.tests;
import android.app.Activity;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.TextView;
import org.chromium.components.devtools_bridge.apiary.ApiaryClientFactory;
import org.chromium.components.devtools_bridge.apiary.TestApiaryClientFactory;
import org.chromium.components.devtools_bridge.ui.GCDRegistrationFragment;
/**
* Activity for testing devtools bridge.
*/
public class DebugActivity extends Activity {
private static final int LAYOUT_ID = 1000;
private LinearLayout mLayout;
@Override
......@@ -33,17 +24,15 @@ public class DebugActivity extends Activity {
super.onCreate(savedInstanceState);
mLayout = new LinearLayout(this);
mLayout.setId(LAYOUT_ID);
mLayout.setOrientation(LinearLayout.VERTICAL);
String intro =
"To test LocalTunnelBridge manually: \n"
+ "1. Enable USB debugging.\n"
+ "2. Run ChromeShell along with this app.\n"
+ "3. Start the LocalTunnelBridge.\n"
+ "4. Connect the device to a desktop via USB.\n"
+ "5. Open chrome://inspect#devices on desktop Chrome.\n"
+ "6. Observe 2 identical Chrome Shells on the device.";
String intro = "To test LocalTunnelBridge manually: \n" +
"1. Enable USB debugging.\n" +
"2. Run ChromeShell along with this app.\n" +
"3. Start the LocalTunnelBridge.\n" +
"4. Connect the device to a desktop via USB.\n" +
"5. Open chrome://inspect#devices on desktop Chrome.\n" +
"6. Observe 2 identical Chrome Shells on the device.";
TextView textView = new TextView(this);
textView.setText(intro);
......@@ -56,12 +45,6 @@ public class DebugActivity extends Activity {
LayoutParams layoutParam = new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT);
getFragmentManager()
.beginTransaction()
.add(LAYOUT_ID, new TestGCDRegistrationFragment())
.commit();
setContentView(mLayout, layoutParam);
}
......@@ -86,49 +69,5 @@ public class DebugActivity extends Activity {
startService(intent);
}
}
private static class TestGCDRegistrationFragment
extends GCDRegistrationFragment implements View.OnClickListener {
private Button mButton;
@Override
public View onCreateView(
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mButton = new Button(getActivity());
mButton.setOnClickListener(this);
updateText();
return mButton;
}
public void updateText() {
mButton.setText(isRegistered()
? "Unregister (registered for " + getOwner() + ")"
: "Register in GCD");
}
@Override
protected void onRegistrationStatusChange() {
updateText();
}
@Override
protected ApiaryClientFactory newClientFactory() {
return new TestApiaryClientFactory();
}
@Override
protected String generateDisplayName() {
return Build.MODEL + " Test app";
}
@Override
public void onClick(View v) {
if (!isRegistered()) {
register();
} else {
unregister();
}
}
}
}
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.components.devtools_bridge.apiary;
/**
* Implementation of ApiaryClientFactory for manual testing.
*/
public class TestApiaryClientFactory extends ApiaryClientFactory {
@Override
public String getAPIKey() {
return "AIzaSyAI1TKbOdqMQ5TltbBT15V5XaIILnDadhI";
}
@Override
public String getOAuthClientId() {
return "287888336735-v2sniebgl82jgm8lpj6mesv982n505iq.apps.googleusercontent.com";
}
@Override
public String[] getGCMSenderIds() {
return new String[] { getGCMSenderId() };
}
public String getOAuthClientSecret() {
return "wdMQhGcQqOZmSNteEbqx0IfY";
}
public String getGCMSenderId() {
return "287888336735";
}
}
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.components.devtools_bridge.util;
import android.util.JsonReader;
import android.util.JsonWriter;
import java.io.StringReader;
import java.io.StringWriter;
/**
* Helper class for testing JSON-based readers.
*/
public class TestSource {
private final StringWriter mSource;
private final JsonWriter mSourceWriter;
public TestSource() {
mSource = new StringWriter();
mSourceWriter = new JsonWriter(mSource);
}
public JsonReader read() {
return new JsonReader(new StringReader(mSource.toString()));
}
public JsonWriter write() {
return mSourceWriter;
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment