blob: 58b2443cb9feda2f5bf9347ff380ae8eaa4750da [file] [log] [blame]
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.sensorprivacy;
import android.annotation.NonNull;
import android.os.Environment;
import android.os.Handler;
import android.util.AtomicFile;
import android.util.Log;
import android.util.Xml;
import com.android.internal.util.XmlUtils;
import com.android.internal.util.dump.DualDumpOutputStream;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
import com.android.server.IoThread;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Objects;
class AllSensorStateController {
private static final String LOG_TAG = AllSensorStateController.class.getSimpleName();
private static final String SENSOR_PRIVACY_XML_FILE = "sensor_privacy.xml";
private static final String XML_TAG_SENSOR_PRIVACY = "all-sensor-privacy";
private static final String XML_TAG_SENSOR_PRIVACY_LEGACY = "sensor-privacy";
private static final String XML_ATTRIBUTE_ENABLED = "enabled";
private static AllSensorStateController sInstance;
private final AtomicFile mAtomicFile =
new AtomicFile(new File(Environment.getDataSystemDirectory(), SENSOR_PRIVACY_XML_FILE));
private boolean mEnabled;
private SensorPrivacyStateController.AllSensorPrivacyListener mListener;
private Handler mListenerHandler;
static AllSensorStateController getInstance() {
if (sInstance == null) {
sInstance = new AllSensorStateController();
}
return sInstance;
}
private AllSensorStateController() {
if (!mAtomicFile.exists()) {
return;
}
try (FileInputStream inputStream = mAtomicFile.openRead()) {
TypedXmlPullParser parser = Xml.resolvePullParser(inputStream);
while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
String tagName = parser.getName();
if (XML_TAG_SENSOR_PRIVACY.equals(tagName)) {
mEnabled |= XmlUtils
.readBooleanAttribute(parser, XML_ATTRIBUTE_ENABLED, false);
break;
}
if (XML_TAG_SENSOR_PRIVACY_LEGACY.equals(tagName)) {
mEnabled |= XmlUtils
.readBooleanAttribute(parser, XML_ATTRIBUTE_ENABLED, false);
}
if ("user".equals(tagName)) { // Migrate from mic/cam toggles format
int user = XmlUtils.readIntAttribute(parser, "id", -1);
if (user == 0) {
mEnabled |=
XmlUtils.readBooleanAttribute(parser, XML_ATTRIBUTE_ENABLED);
}
}
XmlUtils.nextElement(parser);
}
} catch (IOException | XmlPullParserException e) {
Log.e(LOG_TAG, "Caught an exception reading the state from storage: ", e);
mEnabled = false;
}
}
public boolean getAllSensorStateLocked() {
return mEnabled;
}
public void setAllSensorStateLocked(boolean enabled) {
if (mEnabled != enabled) {
mEnabled = enabled;
if (mListener != null && mListenerHandler != null) {
mListenerHandler.sendMessage(
PooledLambda.obtainMessage(mListener::onAllSensorPrivacyChanged, enabled));
}
}
}
void setAllSensorPrivacyListenerLocked(Handler handler,
SensorPrivacyStateController.AllSensorPrivacyListener listener) {
Objects.requireNonNull(handler);
Objects.requireNonNull(listener);
if (mListener != null) {
throw new IllegalStateException("Listener is already set");
}
mListener = listener;
mListenerHandler = handler;
}
public void schedulePersistLocked() {
IoThread.getHandler().sendMessage(PooledLambda.obtainMessage(this::persist, mEnabled));
}
private void persist(boolean enabled) {
FileOutputStream outputStream = null;
try {
outputStream = mAtomicFile.startWrite();
TypedXmlSerializer serializer = Xml.resolveSerializer(outputStream);
serializer.startDocument(null, true);
serializer.startTag(null, XML_TAG_SENSOR_PRIVACY);
serializer.attributeBoolean(null, XML_ATTRIBUTE_ENABLED, enabled);
serializer.endTag(null, XML_TAG_SENSOR_PRIVACY);
serializer.endDocument();
mAtomicFile.finishWrite(outputStream);
} catch (IOException e) {
Log.e(LOG_TAG, "Caught an exception persisting the sensor privacy state: ", e);
mAtomicFile.failWrite(outputStream);
}
}
void resetForTesting() {
mListener = null;
mListenerHandler = null;
mEnabled = false;
}
void dumpLocked(@NonNull DualDumpOutputStream dumpStream) {
// TODO stub
}
}