blob: 7dfbc7c5e05c878832e8f89547a48caaa93ea67c [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* 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.android.dialer.protos;
import android.content.Intent;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import com.android.dialer.common.Assert;
import com.google.protobuf.nano.CodedOutputByteBufferNano;
import com.google.protobuf.nano.InvalidProtocolBufferNanoException;
import com.google.protobuf.nano.MessageNano;
import java.io.IOException;
/** Useful methods for using Protocol Buffers with Android. */
public final class ProtoParsers {
private ProtoParsers() {}
/** Retrieve a proto from a Bundle */
@SuppressWarnings("unchecked") // We want to eventually optimize away parser classes, so cast
public static <T extends MessageNano> T get(Bundle bundle, String key, T defaultInstance)
throws InvalidProtocolBufferNanoException {
InternalDontUse parcelable = bundle.getParcelable(key);
return (T) parcelable.getMessageUnsafe(defaultInstance);
}
/**
* Retrieve a proto from a trusted bundle
*
* @throws RuntimeException if the proto cannot be parsed
*/
public static <T extends MessageNano> T getFromInstanceState(
Bundle bundle, String key, T defaultInstance) {
try {
return get(bundle, key, defaultInstance);
} catch (InvalidProtocolBufferNanoException e) {
throw new RuntimeException(e);
}
}
/**
* Stores a proto in a Bundle, for later retrieval by {@link #get(Bundle, String, MessageNano)} or
* {@link #getFromInstanceState(Bundle, String, MessageNano)}.
*/
public static void put(Bundle bundle, String key, MessageNano message) {
bundle.putParcelable(key, new InternalDontUse<>(null, message));
}
/**
* Stores a proto in an Intent, for later retrieval by {@link #get(Bundle, String, MessageNano)}.
* Needs separate method because Intent has similar to but different API than Bundle.
*/
public static void put(Intent intent, String key, MessageNano message) {
intent.putExtra(key, new InternalDontUse<>(null, message));
}
/** Returns a {@linkplain Parcelable} representation of this protobuf message. */
public static <T extends MessageNano> ParcelableProto<T> asParcelable(T message) {
return new InternalDontUse<>(null, message);
}
/**
* A protobuf message that can be stored in a {@link Parcel}.
*
* <p><b>Note:</b> This <code>Parcelable</code> can only be used in single app. Attempting to send
* it to another app through an <code>Intent</code> will result in an exception due to Proguard
* obfusation when the target application attempts to load the <code>ParcelableProto</code> class.
*/
public interface ParcelableProto<T extends MessageNano> extends Parcelable {
/**
* @throws IllegalStateException if the parceled data does not correspond to the defaultInstance
* type.
*/
T getMessage(T defaultInstance);
}
/** Public because of Parcelable requirements. Do not use. */
public static final class InternalDontUse<T extends MessageNano> implements ParcelableProto<T> {
/* One of these two fields is always populated - since the bytes field never escapes this
* object, there is no risk of concurrent modification by multiple threads, and volatile
* is sufficient to be thread-safe. */
private volatile byte[] bytes;
private volatile T message;
/**
* Ideally, we would have type safety here. However, a static field {@link Creator} is required
* by {@link Parcelable}. Static fields are inherently not type safe, since only 1 exists per
* class (rather than 1 per type).
*/
public static final Parcelable.Creator<InternalDontUse<?>> CREATOR =
new Creator<InternalDontUse<?>>() {
@Override
public InternalDontUse<?> createFromParcel(Parcel parcel) {
int serializedSize = parcel.readInt();
byte[] array = new byte[serializedSize];
parcel.readByteArray(array);
return new InternalDontUse<>(array, null);
}
@Override
public InternalDontUse<?>[] newArray(int i) {
return new InternalDontUse[i];
}
};
private InternalDontUse(byte[] bytes, T message) {
Assert.checkArgument(bytes != null || message != null);
this.bytes = bytes;
this.message = message;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
if (bytes == null) {
final byte[] flatArray = new byte[message.getSerializedSize()];
try {
message.writeTo(CodedOutputByteBufferNano.newInstance(flatArray));
bytes = flatArray;
} catch (IOException impossible) {
throw new AssertionError(impossible);
}
}
parcel.writeInt(bytes.length);
parcel.writeByteArray(bytes);
}
@Override
public T getMessage(T defaultInstance) {
try {
// The proto should never be invalid if it came from our application, so if it is, throw.
return getMessageUnsafe(defaultInstance);
} catch (InvalidProtocolBufferNanoException e) {
throw new IllegalStateException(e);
}
}
@SuppressWarnings("unchecked") // We're being deserialized, so there's no real type safety
T getMessageUnsafe(T defaultInstance) throws InvalidProtocolBufferNanoException {
// There's a risk that we'll double-parse the bytes, but that's OK, because it'll end up
// as the same immutable object anyway.
if (message == null) {
message = MessageNano.mergeFrom(defaultInstance, bytes);
}
return message;
}
}
}