1783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo/*
2783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo * Copyright (C) 2014 The Android Open Source Project
3783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo *
4783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo * Licensed under the Apache License, Version 2.0 (the "License");
5783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo * you may not use this file except in compliance with the License.
6783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo * You may obtain a copy of the License at
7783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo *
8783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo *      http://www.apache.org/licenses/LICENSE-2.0
9783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo *
10783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo * Unless required by applicable law or agreed to in writing, software
11783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo * distributed under the License is distributed on an "AS IS" BASIS,
12783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo * See the License for the specific language governing permissions and
14783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo * limitations under the License.
15783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo */
16783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
17783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seopackage com.android.server.tv;
18783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
19783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seoimport android.content.Context;
20783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seoimport android.content.Intent;
21783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seoimport android.media.tv.TvContentRating;
22783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seoimport android.media.tv.TvInputManager;
238211dda34da967ca1801bb817e0bfc7c3c87863cJae Seoimport android.os.Environment;
24783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seoimport android.os.Handler;
25783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seoimport android.os.UserHandle;
26783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seoimport android.text.TextUtils;
27783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seoimport android.util.AtomicFile;
28783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seoimport android.util.Slog;
29783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seoimport android.util.Xml;
30783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
31783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seoimport com.android.internal.util.FastXmlSerializer;
32783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seoimport com.android.internal.util.XmlUtils;
33783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
34783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seoimport libcore.io.IoUtils;
35783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
36783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seoimport org.xmlpull.v1.XmlPullParser;
37783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seoimport org.xmlpull.v1.XmlPullParserException;
38783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seoimport org.xmlpull.v1.XmlSerializer;
39783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
40783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seoimport java.io.BufferedInputStream;
41783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seoimport java.io.BufferedOutputStream;
42783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seoimport java.io.File;
43783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seoimport java.io.FileNotFoundException;
44783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seoimport java.io.FileOutputStream;
45783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seoimport java.io.IOException;
46783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seoimport java.io.InputStream;
479e9e2e73c6ec7bece20268196dc89ad0c8bafad4Wojciech Staszkiewiczimport java.nio.charset.StandardCharsets;
48783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seoimport java.util.ArrayList;
49404bef8a1de30045d36a7579a00c510dc7f2518dChulwoo Leeimport java.util.Collections;
50783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seoimport java.util.List;
51783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
52783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo/**
53783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo * Manages persistent state recorded by the TV input manager service as an XML file. This class is
54783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo * not thread-safe thus caller must acquire lock on the data store before accessing it. File format:
55783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo * <code>
56783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo * &lt;tv-input-manager-state>
57783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo *   &lt;blocked-ratings>
58783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo *     &lt;rating string="XXXX" />
59783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo *   &lt;/blocked-ratings>
60783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo *   &lt;parental-control enabled="YYYY" />
61783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo * &lt;/tv-input-manager-state>
62783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo * </code>
63783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo */
64783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seofinal class PersistentDataStore {
65783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    private static final String TAG = "TvInputManagerService";
66783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
67783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    private final Context mContext;
68783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
69783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    private final Handler mHandler = new Handler();
70783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
71783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    // The atomic file used to safely read or write the file.
72783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    private final AtomicFile mAtomicFile;
73783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
74404bef8a1de30045d36a7579a00c510dc7f2518dChulwoo Lee    private final List<TvContentRating> mBlockedRatings =
75404bef8a1de30045d36a7579a00c510dc7f2518dChulwoo Lee            Collections.synchronizedList(new ArrayList<TvContentRating>());
76783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
77783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    private boolean mBlockedRatingsChanged;
78783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
79783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    private boolean mParentalControlsEnabled;
80783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
81783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    private boolean mParentalControlsEnabledChanged;
82783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
83783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    // True if the data has been loaded.
84783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    private boolean mLoaded;
85783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
86783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    public PersistentDataStore(Context context, int userId) {
87783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        mContext = context;
888211dda34da967ca1801bb817e0bfc7c3c87863cJae Seo        File userDir = Environment.getUserSystemDirectory(userId);
898211dda34da967ca1801bb817e0bfc7c3c87863cJae Seo        if (!userDir.exists()) {
908211dda34da967ca1801bb817e0bfc7c3c87863cJae Seo            if (!userDir.mkdirs()) {
918211dda34da967ca1801bb817e0bfc7c3c87863cJae Seo                throw new IllegalStateException("User dir cannot be created: " + userDir);
928211dda34da967ca1801bb817e0bfc7c3c87863cJae Seo            }
938211dda34da967ca1801bb817e0bfc7c3c87863cJae Seo        }
94e17b445b6c813f6f9bc93a5e3811128a197ef50bDianne Hackborn        mAtomicFile = new AtomicFile(new File(userDir, "tv-input-manager-state.xml"), "tv-input-state");
95783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    }
96783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
97783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    public boolean isParentalControlsEnabled() {
98783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        loadIfNeeded();
99783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        return mParentalControlsEnabled;
100783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    }
101783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
102783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    public void setParentalControlsEnabled(boolean enabled) {
103276ec84b5db595be54613fa57b9c7f507ec464e6Wonsik Kim        loadIfNeeded();
104783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        if (mParentalControlsEnabled != enabled) {
105783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            mParentalControlsEnabled = enabled;
106783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            mParentalControlsEnabledChanged = true;
107783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            postSave();
108783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        }
109783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    }
110783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
111783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    public boolean isRatingBlocked(TvContentRating rating) {
112783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        loadIfNeeded();
113404bef8a1de30045d36a7579a00c510dc7f2518dChulwoo Lee        synchronized (mBlockedRatings) {
1146e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo            for (TvContentRating blockedRating : mBlockedRatings) {
1156e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo                if (rating.contains(blockedRating)) {
116404bef8a1de30045d36a7579a00c510dc7f2518dChulwoo Lee                    return true;
117404bef8a1de30045d36a7579a00c510dc7f2518dChulwoo Lee                }
118783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            }
119783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        }
120783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        return false;
121783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    }
122783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
123783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    public TvContentRating[] getBlockedRatings() {
124783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        loadIfNeeded();
125783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        return mBlockedRatings.toArray(new TvContentRating[mBlockedRatings.size()]);
126783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    }
127783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
128783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    public void addBlockedRating(TvContentRating rating) {
129276ec84b5db595be54613fa57b9c7f507ec464e6Wonsik Kim        loadIfNeeded();
130783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        if (rating != null && !mBlockedRatings.contains(rating)) {
131783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            mBlockedRatings.add(rating);
132783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            mBlockedRatingsChanged = true;
133783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            postSave();
134783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        }
135783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    }
136783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
137783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    public void removeBlockedRating(TvContentRating rating) {
138276ec84b5db595be54613fa57b9c7f507ec464e6Wonsik Kim        loadIfNeeded();
139783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        if (rating != null && mBlockedRatings.contains(rating)) {
140783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            mBlockedRatings.remove(rating);
141783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            mBlockedRatingsChanged = true;
142783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            postSave();
143783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        }
144783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    }
145783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
146783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    private void loadIfNeeded() {
147783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        if (!mLoaded) {
148783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            load();
149783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            mLoaded = true;
150783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        }
151783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    }
152783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
153783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    private void clearState() {
154783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        mBlockedRatings.clear();
155783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        mParentalControlsEnabled = false;
156783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    }
157783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
158783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    private void load() {
159783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        clearState();
160783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
161783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        final InputStream is;
162783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        try {
163783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            is = mAtomicFile.openRead();
164783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        } catch (FileNotFoundException ex) {
165783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            return;
166783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        }
167783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
168783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        XmlPullParser parser;
169783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        try {
170783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            parser = Xml.newPullParser();
1719e9e2e73c6ec7bece20268196dc89ad0c8bafad4Wojciech Staszkiewicz            parser.setInput(new BufferedInputStream(is), StandardCharsets.UTF_8.name());
172783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            loadFromXml(parser);
173783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        } catch (IOException | XmlPullParserException ex) {
174783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            Slog.w(TAG, "Failed to load tv input manager persistent store data.", ex);
175783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            clearState();
176783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        } finally {
177783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            IoUtils.closeQuietly(is);
178783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        }
179783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    }
180783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
181783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    private void postSave() {
182783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        mHandler.removeCallbacks(mSaveRunnable);
183783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        mHandler.post(mSaveRunnable);
184783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    }
185783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
186783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    /**
187783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo     * Runnable posted when the state needs to be saved. This is used to prevent unnecessary file
188783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo     * operations when multiple settings change in rapid succession.
189783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo     */
190783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    private final Runnable mSaveRunnable = new Runnable() {
191783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        @Override
192783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        public void run() {
193783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            save();
194783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        }
195783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    };
196783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
197783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    private void save() {
198783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        final FileOutputStream os;
199783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        try {
200783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            os = mAtomicFile.startWrite();
201783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            boolean success = false;
202783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            try {
203783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                XmlSerializer serializer = new FastXmlSerializer();
2049e9e2e73c6ec7bece20268196dc89ad0c8bafad4Wojciech Staszkiewicz                serializer.setOutput(new BufferedOutputStream(os), StandardCharsets.UTF_8.name());
205783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                saveToXml(serializer);
206783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                serializer.flush();
207783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                success = true;
208783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            } finally {
209783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                if (success) {
210783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                    mAtomicFile.finishWrite(os);
211783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                    broadcastChangesIfNeeded();
212783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                } else {
213783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                    mAtomicFile.failWrite(os);
214783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                }
215783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            }
216783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        } catch (IOException ex) {
217783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            Slog.w(TAG, "Failed to save tv input manager persistent store data.", ex);
218783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        }
219783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    }
220783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
221783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    private void broadcastChangesIfNeeded() {
222783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        if (mParentalControlsEnabledChanged) {
223783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            mParentalControlsEnabledChanged = false;
224783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            mContext.sendBroadcastAsUser(new Intent(
225783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                    TvInputManager.ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED), UserHandle.ALL);
226783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        }
227783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        if (mBlockedRatingsChanged) {
228783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            mBlockedRatingsChanged = false;
229783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            mContext.sendBroadcastAsUser(new Intent(TvInputManager.ACTION_BLOCKED_RATINGS_CHANGED),
230783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                    UserHandle.ALL);
231783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        }
232783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    }
233783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
234783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    private static final String TAG_TV_INPUT_MANAGER_STATE = "tv-input-manager-state";
235783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    private static final String TAG_BLOCKED_RATINGS = "blocked-ratings";
236783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    private static final String TAG_RATING = "rating";
237783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    private static final String TAG_PARENTAL_CONTROLS = "parental-controls";
238783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    private static final String ATTR_STRING = "string";
239783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    private static final String ATTR_ENABLED = "enabled";
240783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
241783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    private void loadFromXml(XmlPullParser parser)
242783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            throws IOException, XmlPullParserException {
243783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        XmlUtils.beginDocument(parser, TAG_TV_INPUT_MANAGER_STATE);
244783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        final int outerDepth = parser.getDepth();
245783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        while (XmlUtils.nextElementWithin(parser, outerDepth)) {
246783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            if (parser.getName().equals(TAG_BLOCKED_RATINGS)) {
247783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                loadBlockedRatingsFromXml(parser);
248783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            } else if (parser.getName().equals(TAG_PARENTAL_CONTROLS)) {
249783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                String enabled = parser.getAttributeValue(null, ATTR_ENABLED);
250783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                if (TextUtils.isEmpty(enabled)) {
251783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                    throw new XmlPullParserException(
252783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                            "Missing " + ATTR_ENABLED + " attribute on " + TAG_PARENTAL_CONTROLS);
253783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                }
254b0800dce4abdca9aeef1cf72018906c2476c4c6bTobias Thierer                mParentalControlsEnabled = Boolean.parseBoolean(enabled);
255783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            }
256783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        }
257783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    }
258783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
259783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    private void loadBlockedRatingsFromXml(XmlPullParser parser)
260783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            throws IOException, XmlPullParserException {
261783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        final int outerDepth = parser.getDepth();
262783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        while (XmlUtils.nextElementWithin(parser, outerDepth)) {
263783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            if (parser.getName().equals(TAG_RATING)) {
264783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                String ratingString = parser.getAttributeValue(null, ATTR_STRING);
265783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                if (TextUtils.isEmpty(ratingString)) {
266783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                    throw new XmlPullParserException(
267783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                            "Missing " + ATTR_STRING + " attribute on " + TAG_RATING);
268783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                }
269783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo                mBlockedRatings.add(TvContentRating.unflattenFromString(ratingString));
270783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo            }
271783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        }
272783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    }
273783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo
274783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    private void saveToXml(XmlSerializer serializer) throws IOException {
275783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        serializer.startDocument(null, true);
276783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
277783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        serializer.startTag(null, TAG_TV_INPUT_MANAGER_STATE);
278783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        serializer.startTag(null, TAG_BLOCKED_RATINGS);
279404bef8a1de30045d36a7579a00c510dc7f2518dChulwoo Lee        synchronized (mBlockedRatings) {
280404bef8a1de30045d36a7579a00c510dc7f2518dChulwoo Lee            for (TvContentRating rating : mBlockedRatings) {
281404bef8a1de30045d36a7579a00c510dc7f2518dChulwoo Lee                serializer.startTag(null, TAG_RATING);
282404bef8a1de30045d36a7579a00c510dc7f2518dChulwoo Lee                serializer.attribute(null, ATTR_STRING, rating.flattenToString());
283404bef8a1de30045d36a7579a00c510dc7f2518dChulwoo Lee                serializer.endTag(null, TAG_RATING);
284404bef8a1de30045d36a7579a00c510dc7f2518dChulwoo Lee            }
285783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        }
286783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        serializer.endTag(null, TAG_BLOCKED_RATINGS);
287783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        serializer.startTag(null, TAG_PARENTAL_CONTROLS);
288783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        serializer.attribute(null, ATTR_ENABLED, Boolean.toString(mParentalControlsEnabled));
289783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        serializer.endTag(null, TAG_PARENTAL_CONTROLS);
290783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        serializer.endTag(null, TAG_TV_INPUT_MANAGER_STATE);
291783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo        serializer.endDocument();
292783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo    }
293783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo}
294