• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2022 Google LLC
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 package com.google.android.libraries.mobiledatadownload.internal.util;
17 
18 import android.content.Context;
19 import android.content.SharedPreferences;
20 import android.util.Base64;
21 import com.google.common.base.Optional;
22 import com.google.errorprone.annotations.CheckReturnValue;
23 import com.google.protobuf.ExtensionRegistryLite;
24 import com.google.protobuf.InvalidProtocolBufferException;
25 import com.google.protobuf.MessageLite;
26 import com.google.protobuf.Parser;
27 import java.io.IOException;
28 import javax.annotation.Nullable;
29 
30 /**
31  * Simple util to read/write protos from/to {@link SharedPreferences}.
32  *
33  * <p>Protos are serialized, and the binary value is base-64 encoded without padding or wrapping.
34  */
35 @CheckReturnValue
36 public class SharedPreferencesUtil {
37 
38   /**
39    * Reads the shared pref value corresponding to the specified key as a lite proto of type 'T'. The
40    * read value is populated to 'protoValue' which should already be constructed by the caller.
41    *
42    * @return the proto or null if no such element was found or could not be parsed.
43    */
44   @Nullable
readProto( SharedPreferences prefs, K liteKey, Parser<V> parser)45   public static <K extends MessageLite, V extends MessageLite> V readProto(
46       SharedPreferences prefs, K liteKey, Parser<V> parser) {
47     return readProto(prefs, serializeProto(liteKey), parser);
48   }
49 
50   /**
51    * Reads the shared pref value corresponding to the specified key as a lite proto of type 'T'. The
52    * read value is populated to 'protoValue' which should already be constructed by the caller.
53    *
54    * @return the proto or null if no such element was found or could not be parse.
55    */
56   @Nullable
readProto( SharedPreferences prefs, String key, Parser<T> parser)57   public static <T extends MessageLite> T readProto(
58       SharedPreferences prefs, String key, Parser<T> parser) {
59     String encodedLiteString = prefs.getString(key, null);
60     if (encodedLiteString == null) {
61       return null;
62     }
63     try {
64       return parseLiteFromEncodedString(encodedLiteString, parser);
65     } catch (InvalidProtocolBufferException e) {
66       return null;
67     }
68   }
69 
70   /**
71    * Write and commit the serialized form of the proto into pref, corresponding to the give proto
72    * key.
73    */
writeProto( SharedPreferences prefs, final String key, final T protoValue)74   public static <T extends MessageLite> boolean writeProto(
75       SharedPreferences prefs, final String key, final T protoValue) {
76     SharedPreferences.Editor editor = prefs.edit();
77     writeProto(editor, key, protoValue);
78     return editor.commit();
79   }
80 
81   /**
82    * Write and commit the serialized form of the proto into pref, corresponding to the give proto
83    * key.
84    */
writeProto( SharedPreferences prefs, final K protoKey, final T protoValue)85   public static <K extends MessageLite, T extends MessageLite> boolean writeProto(
86       SharedPreferences prefs, final K protoKey, final T protoValue) {
87     SharedPreferences.Editor editor = prefs.edit();
88     writeProto(editor, protoKey, protoValue);
89     return editor.commit();
90   }
91 
92   /**
93    * Write the serialized form of the proto into shared pref, corresponding to the given proto key.
94    */
writeProto( SharedPreferences.Editor editor, final K protoKey, final T protoValue)95   public static <K extends MessageLite, T extends MessageLite> void writeProto(
96       SharedPreferences.Editor editor, final K protoKey, final T protoValue) {
97     writeProto(editor, serializeProto(protoKey), protoValue);
98   }
99 
100   /**
101    * Write the serialized form of the proto into shared pref, corresponding to the given string key.
102    */
writeProto( SharedPreferences.Editor editor, final String key, final T protoValue)103   public static <T extends MessageLite> void writeProto(
104       SharedPreferences.Editor editor, final String key, final T protoValue) {
105     editor.putString(key, serializeProto(protoValue));
106   }
107 
108   /** Removes whatever value corresponds the protoKey from shared prefs. */
removeProto( SharedPreferences prefs, String protoKey)109   public static <K extends MessageLite> boolean removeProto(
110       SharedPreferences prefs, String protoKey) {
111     return prefs.edit().remove(protoKey).commit();
112   }
113 
114   /** Removes whatever value corresponds the protoKey from shared prefs. */
removeProto( SharedPreferences.Editor editor, final K protoKey)115   public static <K extends MessageLite> void removeProto(
116       SharedPreferences.Editor editor, final K protoKey) {
117     editor.remove(serializeProto(protoKey));
118   }
119 
120   /** Removes whatever value corresponds the protoKey from shared prefs. */
removeProto(SharedPreferences.Editor editor, String protoKey)121   public static void removeProto(SharedPreferences.Editor editor, String protoKey) {
122     editor.remove(protoKey);
123   }
124 
125   /** Converts a MessageLite to a string that can be used as a key in shared prefs. */
serializeProto(MessageLite lite)126   public static String serializeProto(MessageLite lite) {
127     byte[] byteValue = lite.toByteArray();
128     return Base64.encodeToString(byteValue, Base64.NO_PADDING | Base64.NO_WRAP);
129   }
130 
131   /**
132    * Parses a MessageLite from the base64 encoded string.
133    *
134    * @return the proto.
135    */
parseLiteFromEncodedString( String base64Encoded, Parser<T> parser)136   public static <T extends MessageLite> T parseLiteFromEncodedString(
137       String base64Encoded, Parser<T> parser) throws InvalidProtocolBufferException {
138     byte[] byteValue;
139     try {
140       byteValue = Base64.decode(base64Encoded, Base64.NO_PADDING | Base64.NO_WRAP);
141     } catch (IllegalArgumentException e) {
142       throw new InvalidProtocolBufferException(
143           "Unable to decode to byte array", new IOException(e));
144     }
145 
146     // Cannot use generated registry here, because it may cause NPE to clients.
147     // For more detail, see b/140135059.
148     return parser.parseFrom(byteValue, ExtensionRegistryLite.getEmptyRegistry());
149   }
150 
151   /** Returns the SharedPreferences name for {@code instanceId}. */
152   // TODO(b/204094591): determine whether instanceId is ever actually null.
getSharedPreferencesName(String baseName, Optional<String> instanceId)153   public static String getSharedPreferencesName(String baseName, Optional<String> instanceId) {
154     return instanceId != null && instanceId.isPresent() ? baseName + instanceId.get() : baseName;
155   }
156 
157   /** Return the SharedPreferences for InstanceId */
158   // TODO(b/204094591): determine whether instanceId is ever actually null.
getSharedPreferences( Context context, String baseName, Optional<String> instanceId)159   public static SharedPreferences getSharedPreferences(
160       Context context, String baseName, Optional<String> instanceId) {
161     return context.getSharedPreferences(
162         getSharedPreferencesName(baseName, instanceId), Context.MODE_PRIVATE);
163   }
164 }
165