• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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