1 /*
2  * Copyright 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.managedprovisioning.common;
18 
19 import android.accounts.Account;
20 import android.content.ComponentName;
21 import android.content.ContentResolver;
22 import android.content.Context;
23 import android.net.Uri;
24 import android.os.PersistableBundle;
25 import android.util.Base64;
26 import java.io.ByteArrayOutputStream;
27 import java.io.File;
28 import java.io.FileInputStream;
29 import java.io.FileOutputStream;
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.io.OutputStream;
33 import java.util.IllformedLocaleException;
34 import java.util.Locale;
35 import java.util.function.Function;
36 
37 /**
38  * Class with Utils methods to store values in xml files, and to convert various
39  * types to and from string.
40  */
41 public class StoreUtils {
42     public static final String ATTR_VALUE = "value";
43 
44     /**
45      * Directory name under parent directory {@link Context#getFilesDir()}
46      * It's directory to cache all files / uri from external provisioning intent.
47      * Files must be prefixed by their own prefixes to avoid collisions.
48      */
49     public static final String DIR_PROVISIONING_PARAMS_FILE_CACHE =
50             "provisioning_params_file_cache";
51 
52     private static final String ATTR_ACCOUNT_NAME = "account-name";
53     private static final String ATTR_ACCOUNT_TYPE = "account-type";
54 
55     /**
56      * Reads an account from a {@link PersistableBundle}.
57      */
persistableBundleToAccount(PersistableBundle bundle)58     public static Account persistableBundleToAccount(PersistableBundle bundle) {
59         return new Account(
60                 bundle.getString(ATTR_ACCOUNT_NAME),
61                 bundle.getString(ATTR_ACCOUNT_TYPE));
62     }
63 
64     /**
65      * Writes an account to a {@link PersistableBundle}.
66      */
accountToPersistableBundle(Account account)67     public static PersistableBundle accountToPersistableBundle(Account account) {
68         final PersistableBundle bundle = new PersistableBundle();
69         bundle.putString(ATTR_ACCOUNT_NAME, account.name);
70         bundle.putString(ATTR_ACCOUNT_TYPE, account.type);
71         return bundle;
72     }
73 
74     /**
75      * Serialize ComponentName.
76      */
componentNameToString(ComponentName componentName)77     public static String componentNameToString(ComponentName componentName) {
78         return componentName == null ? null
79                 : componentName.getPackageName() + "/" + componentName.getClassName();
80     }
81 
82     /**
83      * Deserialize ComponentName.
84      * Don't use {@link ComponentName#unflattenFromString(String)}, because it doesn't keep
85      * original class name
86      */
stringToComponentName(String str)87     public static ComponentName stringToComponentName(String str) {
88         int sep = str.indexOf('/');
89         if (sep < 0 || (sep+1) >= str.length()) {
90             return null;
91         }
92         String pkg = str.substring(0, sep);
93         String cls = str.substring(sep+1);
94         return new ComponentName(pkg, cls);
95     }
96 
97     /**
98      * Converts a String to a Locale.
99      */
stringToLocale(String string)100     public static Locale stringToLocale(String string) throws IllformedLocaleException {
101         if (string != null) {
102             return new Locale.Builder().setLanguageTag(string.replace("_", "-")).build();
103         } else {
104             return null;
105         }
106     }
107 
108     /**
109      * Converts a Locale to a String.
110      */
localeToString(Locale locale)111     public static String localeToString(Locale locale) {
112         if (locale != null) {
113             return locale.getLanguage() + "_" + locale.getCountry();
114         } else {
115             return null;
116         }
117     }
118 
119     /**
120      * Transforms a string into a byte array.
121      *
122      * @param s the string to be transformed
123      */
stringToByteArray(String s)124     public static byte[] stringToByteArray(String s)
125         throws NumberFormatException {
126         try {
127             return Base64.decode(s, Base64.URL_SAFE);
128         } catch (IllegalArgumentException e) {
129             throw new NumberFormatException("Incorrect format. Should be Url-safe Base64 encoded.");
130         }
131     }
132 
133     /**
134      * Transforms a byte array into a string.
135      *
136      * @param bytes the byte array to be transformed
137      */
byteArrayToString(byte[] bytes)138     public static String byteArrayToString(byte[] bytes) {
139         return Base64.encodeToString(bytes, Base64.URL_SAFE | Base64.NO_PADDING | Base64.NO_WRAP);
140     }
141 
putIntegerIfNotNull(PersistableBundle bundle, String attrName, Integer integer)142     public static void putIntegerIfNotNull(PersistableBundle bundle, String attrName,
143             Integer integer) {
144         if (integer != null) {
145             bundle.putInt(attrName, integer);
146         }
147     }
148 
putPersistableBundlableIfNotNull(PersistableBundle bundle, String attrName, PersistableBundlable bundlable)149     public static void putPersistableBundlableIfNotNull(PersistableBundle bundle, String attrName,
150             PersistableBundlable bundlable) {
151         if (bundlable != null) {
152             bundle.putPersistableBundle(attrName, bundlable.toPersistableBundle());
153         }
154     }
155 
getObjectAttrFromPersistableBundle(PersistableBundle bundle, String attrName, Function<PersistableBundle, E> converter)156     public static <E> E getObjectAttrFromPersistableBundle(PersistableBundle bundle,
157             String attrName, Function<PersistableBundle, E> converter) {
158         final PersistableBundle attrBundle = bundle.getPersistableBundle(attrName);
159         return attrBundle == null ? null : converter.apply(attrBundle);
160     }
161 
getStringAttrFromPersistableBundle(PersistableBundle bundle, String attrName, Function<String, E> converter)162     public static <E> E getStringAttrFromPersistableBundle(PersistableBundle bundle,
163             String attrName, Function<String, E> converter) {
164         final String str = bundle.getString(attrName);
165         return str == null ? null : converter.apply(str);
166     }
167 
getIntegerAttrFromPersistableBundle(PersistableBundle bundle, String attrName)168     public static Integer getIntegerAttrFromPersistableBundle(PersistableBundle bundle,
169             String attrName) {
170         return bundle.containsKey(attrName) ? bundle.getInt(attrName) : null;
171     }
172 
173     /**
174      * @return true if successfully copy the uri into the file. Otherwise, the outputFile will not
175      * be created.
176      */
copyUriIntoFile(ContentResolver cr, Uri uri, File outputFile)177     public static boolean copyUriIntoFile(ContentResolver cr, Uri uri, File outputFile) {
178         try (final InputStream in = cr.openInputStream(uri)) { // Throws SecurityException
179             try (final FileOutputStream out = new FileOutputStream(outputFile)) {
180                 copyStream(in, out);
181             }
182             ProvisionLogger.logi("Successfully copy from uri " + uri + " to " + outputFile);
183             return true;
184         } catch (IOException | SecurityException e) {
185             ProvisionLogger.logi("Could not write file from " + uri + " to "
186                     + outputFile, e);
187             // If the file was only partly written, delete it.
188             outputFile.delete();
189             return false;
190         }
191     }
192 
readString(File file)193     public static String readString(File file) throws IOException {
194         try (final InputStream in = new FileInputStream(file)) {
195             try (final ByteArrayOutputStream out = new ByteArrayOutputStream()) {
196                 copyStream(in, out);
197                 return out.toString();
198             }
199         }
200     }
201 
copyStream(final InputStream in, final OutputStream out)202     public static void copyStream(final InputStream in,
203             final OutputStream out) throws IOException {
204         final byte buffer[] = new byte[1024];
205         int bytesReadCount;
206         while ((bytesReadCount = in.read(buffer)) != -1) {
207             out.write(buffer, 0, bytesReadCount);
208         }
209     }
210 
211     public interface TextFileReader {
read(File file)212         String read(File file) throws IOException;
213     }
214 }