1 /* 2 * Copyright (C) 2021 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.server.sensorprivacy; 18 19 import android.annotation.NonNull; 20 import android.os.Environment; 21 import android.os.Handler; 22 import android.util.AtomicFile; 23 import android.util.Log; 24 import android.util.Xml; 25 26 import com.android.internal.util.XmlUtils; 27 import com.android.internal.util.dump.DualDumpOutputStream; 28 import com.android.internal.util.function.pooled.PooledLambda; 29 import com.android.modules.utils.TypedXmlPullParser; 30 import com.android.modules.utils.TypedXmlSerializer; 31 import com.android.server.IoThread; 32 33 import org.xmlpull.v1.XmlPullParser; 34 import org.xmlpull.v1.XmlPullParserException; 35 36 import java.io.File; 37 import java.io.FileInputStream; 38 import java.io.FileOutputStream; 39 import java.io.IOException; 40 import java.util.Objects; 41 42 class AllSensorStateController { 43 44 private static final String LOG_TAG = AllSensorStateController.class.getSimpleName(); 45 46 private static final String SENSOR_PRIVACY_XML_FILE = "sensor_privacy.xml"; 47 private static final String XML_TAG_SENSOR_PRIVACY = "all-sensor-privacy"; 48 private static final String XML_TAG_SENSOR_PRIVACY_LEGACY = "sensor-privacy"; 49 private static final String XML_ATTRIBUTE_ENABLED = "enabled"; 50 51 private static AllSensorStateController sInstance; 52 53 private final AtomicFile mAtomicFile = 54 new AtomicFile(new File(Environment.getDataSystemDirectory(), SENSOR_PRIVACY_XML_FILE)); 55 56 private boolean mEnabled; 57 private SensorPrivacyStateController.AllSensorPrivacyListener mListener; 58 private Handler mListenerHandler; 59 getInstance()60 static AllSensorStateController getInstance() { 61 if (sInstance == null) { 62 sInstance = new AllSensorStateController(); 63 } 64 return sInstance; 65 } 66 AllSensorStateController()67 private AllSensorStateController() { 68 if (!mAtomicFile.exists()) { 69 return; 70 } 71 try (FileInputStream inputStream = mAtomicFile.openRead()) { 72 TypedXmlPullParser parser = Xml.resolvePullParser(inputStream); 73 74 while (parser.getEventType() != XmlPullParser.END_DOCUMENT) { 75 String tagName = parser.getName(); 76 if (XML_TAG_SENSOR_PRIVACY.equals(tagName)) { 77 mEnabled |= XmlUtils 78 .readBooleanAttribute(parser, XML_ATTRIBUTE_ENABLED, false); 79 break; 80 } 81 if (XML_TAG_SENSOR_PRIVACY_LEGACY.equals(tagName)) { 82 mEnabled |= XmlUtils 83 .readBooleanAttribute(parser, XML_ATTRIBUTE_ENABLED, false); 84 } 85 if ("user".equals(tagName)) { // Migrate from mic/cam toggles format 86 int user = XmlUtils.readIntAttribute(parser, "id", -1); 87 if (user == 0) { 88 mEnabled |= 89 XmlUtils.readBooleanAttribute(parser, XML_ATTRIBUTE_ENABLED); 90 } 91 } 92 XmlUtils.nextElement(parser); 93 } 94 } catch (IOException | XmlPullParserException e) { 95 Log.e(LOG_TAG, "Caught an exception reading the state from storage: ", e); 96 mEnabled = false; 97 } 98 } 99 getAllSensorStateLocked()100 public boolean getAllSensorStateLocked() { 101 return mEnabled; 102 } 103 setAllSensorStateLocked(boolean enabled)104 public void setAllSensorStateLocked(boolean enabled) { 105 if (mEnabled != enabled) { 106 mEnabled = enabled; 107 if (mListener != null && mListenerHandler != null) { 108 mListenerHandler.sendMessage( 109 PooledLambda.obtainMessage(mListener::onAllSensorPrivacyChanged, enabled)); 110 } 111 } 112 } 113 setAllSensorPrivacyListenerLocked(Handler handler, SensorPrivacyStateController.AllSensorPrivacyListener listener)114 void setAllSensorPrivacyListenerLocked(Handler handler, 115 SensorPrivacyStateController.AllSensorPrivacyListener listener) { 116 Objects.requireNonNull(handler); 117 Objects.requireNonNull(listener); 118 if (mListener != null) { 119 throw new IllegalStateException("Listener is already set"); 120 } 121 mListener = listener; 122 mListenerHandler = handler; 123 } 124 schedulePersistLocked()125 public void schedulePersistLocked() { 126 IoThread.getHandler().sendMessage(PooledLambda.obtainMessage(this::persist, mEnabled)); 127 } 128 persist(boolean enabled)129 private void persist(boolean enabled) { 130 FileOutputStream outputStream = null; 131 try { 132 outputStream = mAtomicFile.startWrite(); 133 TypedXmlSerializer serializer = Xml.resolveSerializer(outputStream); 134 serializer.startDocument(null, true); 135 serializer.startTag(null, XML_TAG_SENSOR_PRIVACY); 136 serializer.attributeBoolean(null, XML_ATTRIBUTE_ENABLED, enabled); 137 serializer.endTag(null, XML_TAG_SENSOR_PRIVACY); 138 serializer.endDocument(); 139 mAtomicFile.finishWrite(outputStream); 140 } catch (IOException e) { 141 Log.e(LOG_TAG, "Caught an exception persisting the sensor privacy state: ", e); 142 mAtomicFile.failWrite(outputStream); 143 } 144 } 145 resetForTesting()146 void resetForTesting() { 147 mListener = null; 148 mListenerHandler = null; 149 mEnabled = false; 150 } 151 dumpLocked(@onNull DualDumpOutputStream dumpStream)152 void dumpLocked(@NonNull DualDumpOutputStream dumpStream) { 153 // TODO stub 154 } 155 } 156