1 /* 2 * Copyright (C) 2016 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 17 package com.android.dialer.protos; 18 19 import android.content.Intent; 20 import android.os.Bundle; 21 import android.os.Parcel; 22 import android.os.Parcelable; 23 import com.android.dialer.common.Assert; 24 import com.google.protobuf.CodedOutputStream; 25 import com.google.protobuf.InvalidProtocolBufferException; 26 import com.google.protobuf.MessageLite; 27 import java.io.IOException; 28 29 /** Useful methods for using Protocol Buffers with Android. */ 30 public final class ProtoParsers { 31 ProtoParsers()32 private ProtoParsers() {} 33 34 /** Retrieve a proto from a Bundle which was not created within the current executable/version. */ 35 @SuppressWarnings("unchecked") // We want to eventually optimize away parser classes, so cast get(Bundle bundle, String key, T defaultInstance)36 public static <T extends MessageLite> T get(Bundle bundle, String key, T defaultInstance) 37 throws InvalidProtocolBufferException { 38 // Class loaders are unique to each Class instance, so we need to specify how to decode 39 // the information again, even though we set the class loaders when serializing the data. 40 bundle.setClassLoader(ProtoParsers.class.getClassLoader()); 41 InternalDontUse parcelable = bundle.getParcelable(key); 42 return (T) parcelable.getMessageUnsafe(defaultInstance.getDefaultInstanceForType()); 43 } 44 45 /** 46 * Retrieve a proto from a trusted bundle which was created within the current executable/version. 47 * 48 * @throws RuntimeException if the proto cannot be parsed 49 */ getTrusted(Bundle bundle, String key, T defaultInstance)50 public static <T extends MessageLite> T getTrusted(Bundle bundle, String key, T defaultInstance) { 51 try { 52 return get(bundle, key, defaultInstance); 53 } catch (InvalidProtocolBufferException e) { 54 throw new RuntimeException(e); 55 } 56 } 57 58 /** 59 * Retrieve a proto from a trusted bundle which was created within the current executable/version. 60 * 61 * @throws RuntimeException if the proto cannot be parsed 62 */ getTrusted(Intent intent, String key, T defaultInstance)63 public static <T extends MessageLite> T getTrusted(Intent intent, String key, T defaultInstance) { 64 return getTrusted(intent.getExtras(), key, defaultInstance); 65 } 66 67 /** 68 * Stores a proto in a Bundle, for later retrieval by {@link #get(Bundle, String, MessageLite)} or 69 * {@link #getFromInstanceState(Bundle, String, MessageLite)}. 70 */ put(Bundle bundle, String key, MessageLite message)71 public static void put(Bundle bundle, String key, MessageLite message) { 72 bundle.putParcelable(key, new InternalDontUse<>(null, message)); 73 } 74 75 /** 76 * Stores a proto in an Intent, for later retrieval by {@link #get(Bundle, String, MessageLite)}. 77 * Needs separate method because Intent has similar to but different API than Bundle. 78 */ put(Intent intent, String key, MessageLite message)79 public static void put(Intent intent, String key, MessageLite message) { 80 intent.putExtra(key, new InternalDontUse<>(null, message)); 81 } 82 83 /** Returns a {@linkplain Parcelable} representation of this protobuf message. */ asParcelable(T message)84 public static <T extends MessageLite> ParcelableProto<T> asParcelable(T message) { 85 return new InternalDontUse<>(null, message); 86 } 87 88 /** 89 * A protobuf message that can be stored in a {@link Parcel}. 90 * 91 * <p><b>Note:</b> This <code>Parcelable</code> can only be used in single app. Attempting to send 92 * it to another app through an <code>Intent</code> will result in an exception due to Proguard 93 * obfusation when the target application attempts to load the <code>ParcelableProto</code> class. 94 */ 95 public interface ParcelableProto<T extends MessageLite> extends Parcelable { 96 /** 97 * @throws IllegalStateException if the parceled data does not correspond to the defaultInstance 98 * type. 99 */ getMessage(T defaultInstance)100 T getMessage(T defaultInstance); 101 } 102 103 /** Public because of Parcelable requirements. Do not use. */ 104 public static final class InternalDontUse<T extends MessageLite> implements ParcelableProto<T> { 105 /* One of these two fields is always populated - since the bytes field never escapes this 106 * object, there is no risk of concurrent modification by multiple threads, and volatile 107 * is sufficient to be thread-safe. */ 108 private volatile byte[] bytes; 109 private volatile T message; 110 111 /** 112 * Ideally, we would have type safety here. However, a static field {@link Creator} is required 113 * by {@link Parcelable}. Static fields are inherently not type safe, since only 1 exists per 114 * class (rather than 1 per type). 115 */ 116 public static final Parcelable.Creator<InternalDontUse<?>> CREATOR = 117 new Creator<InternalDontUse<?>>() { 118 @Override 119 public InternalDontUse<?> createFromParcel(Parcel parcel) { 120 int serializedSize = parcel.readInt(); 121 byte[] array = new byte[serializedSize]; 122 parcel.readByteArray(array); 123 return new InternalDontUse<>(array, null); 124 } 125 126 @Override 127 public InternalDontUse<?>[] newArray(int i) { 128 return new InternalDontUse[i]; 129 } 130 }; 131 InternalDontUse(byte[] bytes, T message)132 private InternalDontUse(byte[] bytes, T message) { 133 Assert.checkArgument(bytes != null || message != null, "Must have a message or bytes"); 134 this.bytes = bytes; 135 this.message = message; 136 } 137 138 @Override describeContents()139 public int describeContents() { 140 return 0; 141 } 142 143 @Override writeToParcel(Parcel parcel, int i)144 public void writeToParcel(Parcel parcel, int i) { 145 if (bytes == null) { 146 final byte[] flatArray = new byte[message.getSerializedSize()]; 147 try { 148 message.writeTo(CodedOutputStream.newInstance(flatArray)); 149 bytes = flatArray; 150 } catch (IOException impossible) { 151 throw new AssertionError(impossible); 152 } 153 } 154 parcel.writeInt(bytes.length); 155 parcel.writeByteArray(bytes); 156 } 157 158 @Override getMessage(T defaultInstance)159 public T getMessage(T defaultInstance) { 160 try { 161 // The proto should never be invalid if it came from our application, so if it is, throw. 162 return getMessageUnsafe(defaultInstance); 163 } catch (InvalidProtocolBufferException e) { 164 throw new IllegalStateException(e); 165 } 166 } 167 168 @SuppressWarnings("unchecked") // We're being deserialized, so there's no real type safety getMessageUnsafe(T defaultInstance)169 T getMessageUnsafe(T defaultInstance) throws InvalidProtocolBufferException { 170 // There's a risk that we'll double-parse the bytes, but that's OK, because it'll end up 171 // as the same immutable object anyway. 172 if (message == null) { 173 message = (T) defaultInstance.toBuilder().mergeFrom(bytes).build(); 174 } 175 return message; 176 } 177 } 178 179 /** Parses a proto, throwing parser errors as runtime exceptions. */ 180 @SuppressWarnings("unchecked") // We want to eventually optimize away parser classes mergeFrom(byte[] bytes, T defaultInstance)181 public static <T extends MessageLite> T mergeFrom(byte[] bytes, T defaultInstance) { 182 try { 183 return (T) defaultInstance.toBuilder().mergeFrom(bytes).build(); 184 } catch (InvalidProtocolBufferException e) { 185 throw new RuntimeException(e); 186 } 187 } 188 } 189