1/*
2 * Copyright 2014, 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 */
16package com.android.managedprovisioning;
17
18import android.content.ComponentName;
19import android.content.Context;
20import android.content.Intent;
21import android.content.SharedPreferences;
22
23import android.os.Bundle;
24import android.os.PersistableBundle;
25import android.util.Xml;
26
27import java.io.IOException;
28import java.io.StringReader;
29import java.io.StringWriter;
30
31import org.xmlpull.v1.XmlPullParser;
32import org.xmlpull.v1.XmlPullParserException;
33import org.xmlpull.v1.XmlPullParserFactory;
34import org.xmlpull.v1.XmlSerializer;
35
36/**
37 * Helper class to load/save resume information from Intents into a SharedPreferences.
38 */
39public class IntentStore {
40    private SharedPreferences mPrefs;
41    private String mPrefsName; // Name of the file where mPrefs is stored.
42    private Context mContext;
43    private ComponentName mIntentTarget;
44
45    // Key arrays should never be null.
46    private String[] mStringKeys = new String[0];
47    private String[] mLongKeys = new String[0];
48    private String[] mIntKeys = new String[0];
49    private String[] mBooleanKeys = new String[0];
50    private String[] mPersistableBundleKeys = new String[0];
51
52    private static final String TAG_PERSISTABLEBUNDLE = "persistable_bundle";
53
54    private static final String IS_SET = "isSet";
55
56    public IntentStore(Context context, ComponentName intentTarget, String preferencesName) {
57        mContext = context;
58        mPrefsName = preferencesName;
59        mPrefs = context.getSharedPreferences(preferencesName, Context.MODE_PRIVATE);
60        mIntentTarget = intentTarget;
61    }
62
63    public IntentStore setStringKeys(String[] keys) {
64        mStringKeys = (keys == null) ? new String[0] : keys;
65        return this;
66    }
67
68    public IntentStore setLongKeys(String[] keys) {
69        mLongKeys = (keys == null) ? new String[0] : keys;
70        return this;
71    }
72
73    public IntentStore setIntKeys(String[] keys) {
74        mIntKeys = (keys == null) ? new String[0] : keys;
75        return this;
76    }
77
78    public IntentStore setBooleanKeys(String[] keys) {
79        mBooleanKeys = (keys == null) ? new String[0] : keys;
80        return this;
81    }
82
83    public IntentStore setPersistableBundleKeys(String[] keys) {
84        mPersistableBundleKeys = (keys == null) ? new String[0] : keys;
85        return this;
86    }
87
88    public void clear() {
89        mPrefs.edit().clear().commit();
90    }
91
92    public void save(Bundle data){
93        SharedPreferences.Editor editor = mPrefs.edit();
94
95        editor.clear();
96        for (String key : mStringKeys) {
97            editor.putString(key, data.getString(key));
98        }
99        for (String key : mLongKeys) {
100            editor.putLong(key, data.getLong(key));
101        }
102        for (String key : mIntKeys) {
103            editor.putInt(key, data.getInt(key));
104        }
105        for (String key : mBooleanKeys) {
106            editor.putBoolean(key, data.getBoolean(key));
107        }
108        for (String key : mPersistableBundleKeys) {
109
110            // Cast should be guaranteed to succeed by check in the provisioning activities.
111            String bundleString = persistableBundleToString((PersistableBundle) data
112                    .getParcelable(key));
113            if (bundleString != null) {
114                editor.putString(key, bundleString);
115            }
116        }
117        editor.putBoolean(IS_SET, true);
118        editor.commit();
119    }
120
121    public Intent load() {
122        if (!mPrefs.getBoolean(IS_SET, false)) {
123            return null;
124        }
125
126        Intent result = new Intent();
127        result.setComponent(mIntentTarget);
128
129        for (String key : mStringKeys) {
130            String value = mPrefs.getString(key, null);
131            if (value != null) {
132                result.putExtra(key, value);
133            }
134        }
135        for (String key : mLongKeys) {
136            if (mPrefs.contains(key)) {
137                result.putExtra(key, mPrefs.getLong(key, 0));
138            }
139        }
140        for (String key : mIntKeys) {
141            if (mPrefs.contains(key)) {
142                result.putExtra(key, mPrefs.getInt(key, 0));
143            }
144        }
145        for (String key : mBooleanKeys) {
146            if (mPrefs.contains(key)) {
147                result.putExtra(key, mPrefs.getBoolean(key, false));
148            }
149        }
150        for (String key : mPersistableBundleKeys) {
151            if (mPrefs.contains(key)) {
152                PersistableBundle bundle = stringToPersistableBundle(mPrefs.getString(key, null));
153                if (bundle != null) {
154                    result.putExtra(key, bundle);
155                }
156            }
157        }
158
159        return result;
160    }
161
162    private String persistableBundleToString(PersistableBundle bundle) {
163        if (bundle == null) {
164            return null;
165        }
166
167        StringWriter writer = new StringWriter();
168        XmlSerializer serializer = Xml.newSerializer();
169        try {
170            serializer.setOutput(writer);
171            serializer.startDocument(null, true);
172            serializer.startTag(null, TAG_PERSISTABLEBUNDLE);
173            bundle.saveToXml(serializer);
174            serializer.endTag(null, TAG_PERSISTABLEBUNDLE);
175            serializer.endDocument();
176        } catch (IOException|XmlPullParserException e) {
177            ProvisionLogger.loge("Persistable bundle could not be stored as string.", e);
178            return null;
179        }
180
181        return writer.toString();
182    }
183
184    private PersistableBundle stringToPersistableBundle(String string) {
185        if (string == null) {
186            return null;
187        }
188
189        XmlPullParserFactory factory;
190        XmlPullParser parser;
191        try {
192            factory = XmlPullParserFactory.newInstance();
193
194            parser = factory.newPullParser();
195            parser.setInput(new StringReader(string));
196
197            if (parser.next() == XmlPullParser.START_TAG) {
198                if (TAG_PERSISTABLEBUNDLE.equals(parser.getName())) {
199                    return PersistableBundle.restoreFromXml(parser);
200                }
201            }
202        } catch (IOException|XmlPullParserException e) {
203            ProvisionLogger.loge(e);
204            // Fall through.
205        }
206        ProvisionLogger.loge("Persistable bundle could not be restored from string " + string);
207        return null;
208    }
209}
210