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 * <tv-input-manager-state> 57783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo * <blocked-ratings> 58783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo * <rating string="XXXX" /> 59783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo * </blocked-ratings> 60783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo * <parental-control enabled="YYYY" /> 61783645e99f909ffc7a2d5d2fca9324cc0e9b7362Jae Seo * </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