blob: a1bf109b16667ad9d228ddf5e83f875ec17478da [file] [log] [blame]
/*
* Copyright (C) 2018 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.textclassifier;
import android.content.Context;
import android.database.ContentObserver;
import android.provider.Settings;
import androidx.annotation.GuardedBy;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import java.lang.ref.WeakReference;
import java.util.Objects;
import java.util.function.Supplier;
import javax.annotation.Nullable;
/** Parses the {@link Settings.Global#TEXT_CLASSIFIER_ACTION_MODEL_PARAMS} flag. */
// TODO(tonymak): Re-enable this class.
public final class ActionsModelParamsSupplier
implements Supplier<ActionsModelParamsSupplier.ActionsModelParams> {
private static final String TAG = "ActionsModelParams";
@VisibleForTesting static final String KEY_REQUIRED_MODEL_VERSION = "required_model_version";
@VisibleForTesting static final String KEY_REQUIRED_LOCALES = "required_locales";
@VisibleForTesting static final String KEY_SERIALIZED_PRECONDITIONS = "serialized_preconditions";
private final Context appContext;
private final SettingsObserver settingsObserver;
private final Object lock = new Object();
private final Runnable onChangedListener;
@Nullable
@GuardedBy("lock")
private ActionsModelParams actionsModelParams;
@GuardedBy("lock")
private boolean parsed = true;
public ActionsModelParamsSupplier(Context context, @Nullable Runnable onChangedListener) {
appContext = Preconditions.checkNotNull(context).getApplicationContext();
this.onChangedListener = onChangedListener == null ? () -> {} : onChangedListener;
settingsObserver =
new SettingsObserver(
appContext,
() -> {
synchronized (lock) {
TcLog.v(TAG, "Settings.Global.TEXT_CLASSIFIER_ACTION_MODEL_PARAMS is updated");
parsed = true;
this.onChangedListener.run();
}
});
}
/**
* Returns the parsed actions params or {@link ActionsModelParams#INVALID} if the value is
* invalid.
*/
@Override
public ActionsModelParams get() {
synchronized (lock) {
if (parsed) {
actionsModelParams = parse();
parsed = false;
}
return actionsModelParams;
}
}
private static ActionsModelParams parse() {
// String settingStr = Settings.Global.getString(contentResolver,
// Settings.Global.TEXT_CLASSIFIER_ACTION_MODEL_PARAMS);
// if (TextUtils.isEmpty(settingStr)) {
// return ActionsModelParams.INVALID;
// }
// try {
// KeyValueListParser keyValueListParser = new KeyValueListParser(',');
// keyValueListParser.setString(settingStr);
// int version = keyValueListParser.getInt(KEY_REQUIRED_MODEL_VERSION, -1);
// if (version == -1) {
// TcLog.w(TAG, "ActionsModelParams.Parse, invalid model version");
// return ActionsModelParams.INVALID;
// }
// String locales = keyValueListParser.getString(KEY_REQUIRED_LOCALES, null);
// if (locales == null) {
// TcLog.w(TAG, "ActionsModelParams.Parse, invalid locales");
// return ActionsModelParams.INVALID;
// }
// String serializedPreconditionsStr =
// keyValueListParser.getString(KEY_SERIALIZED_PRECONDITIONS, null);
// if (serializedPreconditionsStr == null) {
// TcLog.w(TAG, "ActionsModelParams.Parse, invalid preconditions");
// return ActionsModelParams.INVALID;
// }
// byte[] serializedPreconditions =
// Base64.decode(serializedPreconditionsStr, Base64.NO_WRAP);
// return new ActionsModelParams(version, locales, serializedPreconditions);
// } catch (Throwable t) {
// TcLog.e(TAG, "Invalid TEXT_CLASSIFIER_ACTION_MODEL_PARAMS, ignore", t);
// }
return ActionsModelParams.INVALID;
}
@Override
protected void finalize() throws Throwable {
try {
appContext.getContentResolver().unregisterContentObserver(settingsObserver);
} finally {
super.finalize();
}
}
/** Represents the parsed result. */
public static final class ActionsModelParams {
public static final ActionsModelParams INVALID = new ActionsModelParams(-1, "", new byte[0]);
/** The required model version to apply {@code serializedPreconditions}. */
private final int requiredModelVersion;
/** The required model locales to apply {@code serializedPreconditions}. */
private final String requiredModelLocales;
/**
* The serialized params that will be applied to the model file, if all requirements are met. Do
* not modify.
*/
private final byte[] serializedPreconditions;
public ActionsModelParams(
int requiredModelVersion, String requiredModelLocales, byte[] serializedPreconditions) {
this.requiredModelVersion = requiredModelVersion;
this.requiredModelLocales = Preconditions.checkNotNull(requiredModelLocales);
this.serializedPreconditions = Preconditions.checkNotNull(serializedPreconditions);
}
/**
* Returns the serialized preconditions. Returns {@code null} if the model in use does not meet
* all the requirements listed in the {@code ActionsModelParams} or the params are invalid.
*/
@Nullable
public byte[] getSerializedPreconditions(ModelFileManager.ModelFile modelInUse) {
if (this == INVALID) {
return null;
}
if (modelInUse.getVersion() != requiredModelVersion) {
TcLog.w(
TAG,
String.format(
"Not applying mSerializedPreconditions, required version=%d, actual=%d",
requiredModelVersion, modelInUse.getVersion()));
return null;
}
if (!Objects.equals(modelInUse.getSupportedLocalesStr(), requiredModelLocales)) {
TcLog.w(
TAG,
String.format(
"Not applying mSerializedPreconditions, required locales=%s, actual=%s",
requiredModelLocales, modelInUse.getSupportedLocalesStr()));
return null;
}
return serializedPreconditions;
}
}
private static final class SettingsObserver extends ContentObserver {
private final WeakReference<Runnable> onChangedListener;
SettingsObserver(Context appContext, Runnable listener) {
super(null);
onChangedListener = new WeakReference<>(listener);
// appContext.getContentResolver().registerContentObserver(
//
// Settings.Global.getUriFor(Settings.Global.TEXT_CLASSIFIER_ACTION_MODEL_PARAMS),
// false /* notifyForDescendants */,
// this);
}
@Override
public void onChange(boolean selfChange) {
if (onChangedListener.get() != null) {
onChangedListener.get().run();
}
}
}
}