1 /*
2  * Copyright (C) 2020 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.keychain;
18 
19 import android.annotation.Nullable;
20 import android.security.CredentialManagementApp;
21 import android.util.AtomicFile;
22 import android.util.Log;
23 import android.util.Xml;
24 
25 import com.android.internal.util.FastXmlSerializer;
26 
27 import org.xmlpull.v1.XmlPullParser;
28 import org.xmlpull.v1.XmlPullParserException;
29 import org.xmlpull.v1.XmlSerializer;
30 
31 import java.io.File;
32 import java.io.FileInputStream;
33 import java.io.FileOutputStream;
34 import java.io.IOException;
35 import java.nio.charset.StandardCharsets;
36 
37 public class KeyChainStateStorage {
38 
39     private static final String TAG = "KeyChain";
40     private static final String TAG_CREDENTIAL_MANAGEMENT_APP = "credential-management-app";
41 
42     private final File mDirectory;
43 
KeyChainStateStorage(File directory)44     public KeyChainStateStorage(File directory) {
45         mDirectory = directory;
46     }
47 
48     @Nullable
loadCredentialManagementApp()49     public CredentialManagementApp loadCredentialManagementApp() {
50         CredentialManagementApp credentialManagementApp = null;
51         AtomicFile file = getCredentialManagementFile();
52         FileInputStream stream = null;
53         try {
54             stream = file.openRead();
55             XmlPullParser parser = Xml.newPullParser();
56             parser.setInput(stream, StandardCharsets.UTF_8.name());
57             int type;
58             while ((type = parser.next()) != XmlPullParser.START_TAG
59                     && type != XmlPullParser.END_DOCUMENT) {
60             }
61             String tag = parser.getName();
62             if (TAG_CREDENTIAL_MANAGEMENT_APP.equals(tag)) {
63                 credentialManagementApp = CredentialManagementApp.readFromXml(parser);
64             }
65         } catch (XmlPullParserException | IOException e) {
66             Log.e(TAG, "Failed to load state", e);
67         } finally {
68             try {
69                 if (stream != null) {
70                     stream.close();
71                 }
72             } catch (IOException e) {
73             }
74         }
75         return credentialManagementApp;
76     }
77 
saveCredentialManagementApp( @ullable CredentialManagementApp credentialManagementApp)78     public void saveCredentialManagementApp(
79             @Nullable CredentialManagementApp credentialManagementApp) {
80         AtomicFile file = getCredentialManagementFile();
81         FileOutputStream stream;
82         try {
83             stream = file.startWrite();
84         } catch (IOException e) {
85             Log.e(TAG, "Failed to write state " + e);
86             return;
87         }
88         try {
89             XmlSerializer out = new FastXmlSerializer();
90             out.setOutput(stream, StandardCharsets.UTF_8.name());
91             out.startDocument(null, true);
92 
93             if (credentialManagementApp != null) {
94                 out.startTag(null, TAG_CREDENTIAL_MANAGEMENT_APP);
95                 credentialManagementApp.writeToXml(out);
96                 out.endTag(null, TAG_CREDENTIAL_MANAGEMENT_APP);
97             }
98             out.endDocument();
99             file.finishWrite(stream);
100             stream.close();
101         } catch (IOException e) {
102             Log.e(TAG, "Failed to store state");
103             file.failWrite(stream);
104         }
105     }
106 
getCredentialManagementFile()107     private AtomicFile getCredentialManagementFile() {
108         File file = new File(mDirectory, "credential-management-app.xml");
109         if (!file.exists()) {
110             try {
111                 file.createNewFile();
112             } catch (IOException e) {
113             }
114         }
115         return new AtomicFile(file);
116     }
117 }
118