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