| /* |
| * Copyright 2011 Google Inc. |
| * |
| * 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.google.ipc.invalidation.ticl.android2; |
| |
| import com.google.common.base.Joiner; |
| import com.google.ipc.invalidation.common.CommonProtoStrings2; |
| import com.google.ipc.invalidation.util.TextBuilder; |
| import com.google.protobuf.ByteString; |
| import com.google.protobuf.InvalidProtocolBufferException; |
| import com.google.protos.ipc.invalidation.AndroidService.AndroidNetworkSendRequest; |
| import com.google.protos.ipc.invalidation.AndroidService.AndroidSchedulerEvent; |
| import com.google.protos.ipc.invalidation.AndroidService.ClientDowncall; |
| import com.google.protos.ipc.invalidation.AndroidService.ClientDowncall.AckDowncall; |
| import com.google.protos.ipc.invalidation.AndroidService.ClientDowncall.RegistrationDowncall; |
| import com.google.protos.ipc.invalidation.AndroidService.InternalDowncall; |
| import com.google.protos.ipc.invalidation.AndroidService.InternalDowncall.CreateClient; |
| import com.google.protos.ipc.invalidation.AndroidService.InternalDowncall.NetworkStatus; |
| import com.google.protos.ipc.invalidation.AndroidService.InternalDowncall.ServerMessage; |
| import com.google.protos.ipc.invalidation.AndroidService.ListenerUpcall; |
| import com.google.protos.ipc.invalidation.AndroidService.ListenerUpcall.ErrorUpcall; |
| import com.google.protos.ipc.invalidation.AndroidService.ListenerUpcall.InvalidateUpcall; |
| import com.google.protos.ipc.invalidation.AndroidService.ListenerUpcall.RegistrationFailureUpcall; |
| import com.google.protos.ipc.invalidation.AndroidService.ListenerUpcall.RegistrationStatusUpcall; |
| import com.google.protos.ipc.invalidation.AndroidService.ListenerUpcall.ReissueRegistrationsUpcall; |
| import com.google.protos.ipc.invalidation.Client.AckHandleP; |
| import com.google.protos.ipc.invalidation.ClientProtocol.ObjectIdP; |
| |
| import android.content.Intent; |
| |
| import java.util.List; |
| |
| |
| /** |
| * Utilities to format Android protocol buffers and intents as compact strings suitable for logging. |
| * By convention, methods take a {@link TextBuilder} and the object to format and return the |
| * builder. Null object arguments are permitted. |
| * |
| * <p>{@link #toCompactString} methods immediately append a description of the object to the given |
| * {@link TextBuilder}s. {@link #toLazyCompactString} methods return an object that defers the work |
| * of formatting the provided argument until {@link Object#toString} is called. |
| * |
| */ |
| public class AndroidStrings { |
| |
| /** |
| * String to return when the argument is unknown (suggests a new protocol field or invalid |
| * proto). |
| */ |
| static final String UNKNOWN_MESSAGE = "UNKNOWN@AndroidStrings"; |
| |
| /** |
| * String to return when there is an error formatting an argument. |
| */ |
| static final String ERROR_MESSAGE = "ERROR@AndroidStrings"; |
| |
| /** |
| * Returns an object that lazily evaluates {@link #toCompactString} when {@link Object#toString} |
| * is called. |
| */ |
| public static Object toLazyCompactString(final Intent intent) { |
| return new Object() { |
| @Override |
| public String toString() { |
| TextBuilder builder = new TextBuilder(); |
| AndroidStrings.toCompactString(builder, intent); |
| return builder.toString(); |
| } |
| }; |
| } |
| |
| /** Appends a description of the given client {@code downcall} to the given {@code builder}. */ |
| public static TextBuilder toCompactString(TextBuilder builder, |
| ClientDowncall downcall) { |
| if (downcall == null) { |
| return builder; |
| } |
| builder.append(ProtocolIntents.CLIENT_DOWNCALL_KEY).append("::"); |
| if (downcall.hasStart()) { |
| builder.append("start()"); |
| } else if (downcall.hasStop()) { |
| builder.append("stop()"); |
| } else if (downcall.hasAck()) { |
| toCompactString(builder, downcall.getAck()); |
| } else if (downcall.hasRegistrations()) { |
| toCompactString(builder, downcall.getRegistrations()); |
| } else { |
| builder.append(UNKNOWN_MESSAGE); |
| } |
| return builder; |
| } |
| |
| /** Appends a description of the given {@code ack} downcall to the given {@code builder}. */ |
| public static TextBuilder toCompactString(TextBuilder builder, AckDowncall ack) { |
| if (ack == null) { |
| return builder; |
| } |
| builder.append("ack("); |
| serializedAckHandleToCompactString(builder, ack.getAckHandle()); |
| return builder.append(")"); |
| } |
| |
| /** |
| * Appends a description of the given {@code registration} downcall to the given {@code builder}. |
| */ |
| public static TextBuilder toCompactString(TextBuilder builder, |
| RegistrationDowncall registration) { |
| if (registration == null) { |
| return builder; |
| } |
| List<ObjectIdP> objects; |
| if (registration.getRegistrationsCount() > 0) { |
| builder.append("register("); |
| objects = registration.getRegistrationsList(); |
| } else { |
| builder.append("unregister("); |
| objects = registration.getUnregistrationsList(); |
| } |
| return CommonProtoStrings2.toCompactStringForObjectIds(builder, objects).append(")"); |
| } |
| |
| /** Appends a description of the given internal {@code downcall} to the given {@code builder}. */ |
| public static TextBuilder toCompactString(TextBuilder builder, |
| InternalDowncall downcall) { |
| if (downcall == null) { |
| return builder; |
| } |
| builder.append(ProtocolIntents.INTERNAL_DOWNCALL_KEY).append("::"); |
| if (downcall.hasServerMessage()) { |
| toCompactString(builder, downcall.getServerMessage()); |
| } else if (downcall.hasNetworkStatus()) { |
| toCompactString(builder, downcall.getNetworkStatus()); |
| } else if (downcall.hasNetworkAddrChange()) { |
| builder.append("newtworkAddrChange()"); |
| } else if (downcall.hasCreateClient()) { |
| toCompactString(builder, downcall.getCreateClient()); |
| } else { |
| builder.append(UNKNOWN_MESSAGE); |
| } |
| return builder; |
| } |
| |
| /** Appends a description of the given {@code serverMessage} to the given {@code builder}. */ |
| public static TextBuilder toCompactString(TextBuilder builder, |
| ServerMessage serverMessage) { |
| if (serverMessage == null) { |
| return builder; |
| } |
| return builder.append("serverMessage(").append(serverMessage.getData()).append(")"); |
| } |
| |
| /** Appends a description of the given {@code networkStatus} to the given {@code builder}. */ |
| public static TextBuilder toCompactString(TextBuilder builder, |
| NetworkStatus networkStatus) { |
| if (networkStatus == null) { |
| return builder; |
| } |
| return builder.append("networkStatus(isOnline = ").append(networkStatus.getIsOnline()) |
| .append(")"); |
| } |
| |
| /** |
| * Appends a description of the given {@code createClient} command to the given {@code builder}. |
| */ |
| public static TextBuilder toCompactString(TextBuilder builder, |
| CreateClient createClient) { |
| if (createClient == null) { |
| return builder; |
| } |
| return builder.append("createClient(type = ").append(createClient.getClientType()) |
| .append(", name = ").append(createClient.getClientName()).append(", skipStartForTest = ") |
| .append(createClient.getSkipStartForTest()).append(")"); |
| } |
| |
| /** Appends a description of the given listener {@code upcall} to the given {@code builder}. */ |
| public static TextBuilder toCompactString(TextBuilder builder, |
| ListenerUpcall upcall) { |
| if (upcall == null) { |
| return builder; |
| } |
| builder.append(ProtocolIntents.LISTENER_UPCALL_KEY).append("::"); |
| if (upcall.hasReady()) { |
| builder.append(".ready()"); |
| } else if (upcall.hasInvalidate()) { |
| toCompactString(builder, upcall.getInvalidate()); |
| } else if (upcall.hasRegistrationStatus()) { |
| toCompactString(builder, upcall.getRegistrationStatus()); |
| } else if (upcall.hasRegistrationFailure()) { |
| toCompactString(builder, upcall.getRegistrationFailure()); |
| } else if (upcall.hasReissueRegistrations()) { |
| toCompactString(builder, upcall.getReissueRegistrations()); |
| } else if (upcall.hasError()) { |
| toCompactString(builder, upcall.getError()); |
| } else { |
| builder.append(UNKNOWN_MESSAGE); |
| } |
| return builder; |
| } |
| |
| /** Appends a description of the given {@code invalidate} command to the given {@code builder}. */ |
| public static TextBuilder toCompactString(TextBuilder builder, |
| InvalidateUpcall invalidate) { |
| if (invalidate == null) { |
| return builder; |
| } |
| builder.append("invalidate(ackHandle = "); |
| serializedAckHandleToCompactString(builder, invalidate.getAckHandle()); |
| builder.append(", "); |
| if (invalidate.hasInvalidation()) { |
| CommonProtoStrings2.toCompactString(builder, invalidate.getInvalidation()); |
| } else if (invalidate.getInvalidateAll()) { |
| builder.append("ALL"); |
| } else if (invalidate.hasInvalidateUnknown()) { |
| builder.append("UNKNOWN: "); |
| CommonProtoStrings2.toCompactString(builder, invalidate.getInvalidateUnknown()); |
| } |
| return builder.append(")"); |
| } |
| |
| /** Appends a description of the given {@code status} upcall to the given {@code builder}. */ |
| public static TextBuilder toCompactString(TextBuilder builder, |
| RegistrationStatusUpcall status) { |
| if (status == null) { |
| return builder; |
| } |
| builder.append("registrationStatus(objectId = "); |
| CommonProtoStrings2.toCompactString(builder, status.getObjectId()); |
| return builder.append(", isRegistered = ").append(status.getIsRegistered()).append(")"); |
| } |
| |
| /** Appends a description of the given {@code failure} upcall to the given {@code builder}. */ |
| public static TextBuilder toCompactString(TextBuilder builder, |
| RegistrationFailureUpcall failure) { |
| if (failure == null) { |
| return builder; |
| } |
| builder.append("registrationFailure(objectId = "); |
| CommonProtoStrings2.toCompactString(builder, failure.getObjectId()); |
| return builder.append(", isTransient = ").append(failure.getTransient()).append(")"); |
| } |
| |
| /** |
| * Appends a description of the given {@code reissue} registrations upcall to the given |
| * {@code builder}. |
| */ |
| public static TextBuilder toCompactString(TextBuilder builder, |
| ReissueRegistrationsUpcall reissue) { |
| if (reissue == null) { |
| return builder; |
| } |
| builder.append("reissueRegistrations(prefix = "); |
| return builder.append(reissue.getPrefix()).append(", length = ").append(reissue.getLength()) |
| .append(")"); |
| } |
| |
| /** Appends a description of the given {@code error} upcall to the given {@code builder}. */ |
| public static TextBuilder toCompactString(TextBuilder builder, ErrorUpcall error) { |
| if (error == null) { |
| return builder; |
| } |
| return builder.append("error(code = ").append(error.getErrorCode()).append(", message = ") |
| .append(error.getErrorMessage()).append(", isTransient = ").append(error.getIsTransient()) |
| .append(")"); |
| } |
| |
| /** Appends a description of the given {@code request} to the given {@code builder}. */ |
| public static TextBuilder toCompactString(TextBuilder builder, |
| AndroidNetworkSendRequest request) { |
| if (request == null) { |
| return builder; |
| } |
| return builder.append(ProtocolIntents.OUTBOUND_MESSAGE_KEY).append("(") |
| .append(request.getMessage()).append(")"); |
| } |
| |
| /** Appends a description of the given (@code event} to the given {@code builder}. */ |
| public static TextBuilder toCompactString(TextBuilder builder, |
| AndroidSchedulerEvent event) { |
| if (event == null) { |
| return builder; |
| } |
| return builder.append(ProtocolIntents.SCHEDULER_KEY).append("(eventName = ") |
| .append(event.getEventName()).append(", ticlId = ").append(event.getTiclId()).append(")"); |
| } |
| |
| /** See spec in implementation notes. */ |
| public static TextBuilder toCompactString(TextBuilder builder, AckHandleP ackHandle) { |
| if (ackHandle == null) { |
| return builder; |
| } |
| return CommonProtoStrings2.toCompactString(builder.appendFormat("AckHandle: "), |
| ackHandle.getInvalidation()); |
| } |
| |
| /** |
| * Appends a description of the given {@code intent} to the given {@code builder}. If the intent |
| * includes some recognized extras, formats the extra context as well. |
| */ |
| public static TextBuilder toCompactString(TextBuilder builder, Intent intent) { |
| if (intent == null) { |
| return builder; |
| } |
| builder.append("intent("); |
| try { |
| if (!tryParseExtra(builder, intent)) { |
| builder.append(UNKNOWN_MESSAGE).append(", extras = ") |
| .append(Joiner.on(", ").join(intent.getExtras().keySet())); |
| } |
| } catch (InvalidProtocolBufferException exception) { |
| builder.append(ERROR_MESSAGE).append(" : ").append(exception); |
| } |
| return builder.append(")"); |
| } |
| |
| /** Appends a description of any known extra or appends 'UNKNOWN' if none are recognized. */ |
| private static boolean tryParseExtra(TextBuilder builder, Intent intent) |
| throws InvalidProtocolBufferException { |
| byte[] data; |
| |
| data = intent.getByteArrayExtra(ProtocolIntents.SCHEDULER_KEY); |
| if (data != null) { |
| AndroidSchedulerEvent schedulerEvent = AndroidSchedulerEvent.parseFrom(data); |
| toCompactString(builder, schedulerEvent); |
| return true; |
| } |
| |
| data = intent.getByteArrayExtra(ProtocolIntents.OUTBOUND_MESSAGE_KEY); |
| if (data != null) { |
| AndroidNetworkSendRequest outboundMessage = AndroidNetworkSendRequest.parseFrom(data); |
| toCompactString(builder, outboundMessage); |
| return true; |
| } |
| |
| data = intent.getByteArrayExtra(ProtocolIntents.LISTENER_UPCALL_KEY); |
| if (data != null) { |
| ListenerUpcall upcall = ListenerUpcall.parseFrom(data); |
| toCompactString(builder, upcall); |
| return true; |
| } |
| |
| data = intent.getByteArrayExtra(ProtocolIntents.INTERNAL_DOWNCALL_KEY); |
| if (data != null) { |
| InternalDowncall internalDowncall = InternalDowncall.parseFrom(data); |
| toCompactString(builder, internalDowncall); |
| return true; |
| } |
| |
| data = intent.getByteArrayExtra(ProtocolIntents.CLIENT_DOWNCALL_KEY); |
| if (data != null) { |
| ClientDowncall clientDowncall = ClientDowncall.parseFrom(data); |
| toCompactString(builder, clientDowncall); |
| return true; |
| } |
| |
| // Didn't recognize any intents. |
| return false; |
| } |
| |
| /** Given serialized form of an ack handle, appends description to {@code builder}. */ |
| private static TextBuilder serializedAckHandleToCompactString( |
| TextBuilder builder, ByteString serialized) { |
| if (serialized == null) { |
| return builder; |
| } |
| // The ack handle is supposed by an AckHandleP! |
| try { |
| AckHandleP ackHandle = AckHandleP.parseFrom(serialized); |
| return toCompactString(builder, ackHandle); |
| } catch (InvalidProtocolBufferException exception) { |
| // But it wasn't... Just log the raw bytes. |
| return builder.append(serialized); |
| } |
| } |
| |
| private AndroidStrings() { |
| // Avoid instantiation. |
| } |
| } |