diff options
7 files changed, 356 insertions, 8 deletions
diff --git a/nfc/java/android/nfc/NfcAdapter.java b/nfc/java/android/nfc/NfcAdapter.java index 951702ceef0b..d9fd42fd4f7a 100644 --- a/nfc/java/android/nfc/NfcAdapter.java +++ b/nfc/java/android/nfc/NfcAdapter.java @@ -1150,8 +1150,9 @@ public final class NfcAdapter { } /** - * Pauses polling for a {@code timeoutInMs} millis. If polling must be resumed before timeout, - * use {@link #resumePolling()}. + * Pauses NFC tag reader mode polling for a {@code timeoutInMs} millisecond. + * In case of {@code timeoutInMs} is zero or invalid polling will be stopped indefinitely + * use {@link #resumePolling() to resume the polling. * @hide */ public void pausePolling(int timeoutInMs) { @@ -1210,9 +1211,8 @@ public final class NfcAdapter { } /** - * Resumes default polling for the current device state if polling is paused. Calling - * this while polling is not paused is a no-op. - * + * Resumes default NFC tag reader mode polling for the current device state if polling is + * paused. Calling this while already in polling is a no-op. * @hide */ public void resumePolling() { diff --git a/nfc/java/android/nfc/NfcOemExtension.java b/nfc/java/android/nfc/NfcOemExtension.java index bc410c7b8ba5..905d6f68a8a0 100644 --- a/nfc/java/android/nfc/NfcOemExtension.java +++ b/nfc/java/android/nfc/NfcOemExtension.java @@ -569,8 +569,9 @@ public final class NfcOemExtension { } /** - * Pauses NFC tag reader mode polling for a {@code timeoutInMs} millisecond. If polling must be - * resumed before timeout, use {@link #resumePolling()}. + * Pauses NFC tag reader mode polling for a {@code timeoutInMs} millisecond. + * In case of {@code timeoutInMs} is zero or invalid polling will be stopped indefinitely + * use {@link #resumePolling() to resume the polling. * @param timeoutInMs the pause polling duration in millisecond */ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) @@ -581,7 +582,7 @@ public final class NfcOemExtension { /** * Resumes default NFC tag reader mode polling for the current device state if polling is - * paused. Calling this while polling is not paused is a no-op. + * paused. Calling this while already in polling is a no-op. */ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp index 8e884bc3484b..d9182010c1cb 100644 --- a/ravenwood/Android.bp +++ b/ravenwood/Android.bp @@ -124,6 +124,7 @@ java_library { ], libs: [ "framework-minus-apex.ravenwood", + "framework-configinfrastructure.ravenwood", "ravenwood-helper-libcore-runtime", ], sdk_version: "core_current", @@ -395,6 +396,9 @@ android_ravenwood_libgroup { "icu4j-icudata-jarjar", "icu4j-icutzdata-jarjar", + // DeviceConfig + "framework-configinfrastructure.ravenwood", + // Provide runtime versions of utils linked in below "junit", "truth", diff --git a/ravenwood/Framework.bp b/ravenwood/Framework.bp index 5cb1479514a3..1bea434f5b49 100644 --- a/ravenwood/Framework.bp +++ b/ravenwood/Framework.bp @@ -290,3 +290,57 @@ java_genrule { "core-icu4j-for-host.ravenwood.jar", ], } + +/////////////////////////////////// +// framework-configinfrastructure +/////////////////////////////////// + +java_genrule { + name: "framework-configinfrastructure.ravenwood-base", + tools: ["hoststubgen"], + cmd: "$(location hoststubgen) " + + "@$(location :ravenwood-standard-options) " + + + "--debug-log $(location framework-configinfrastructure.log) " + + "--stats-file $(location framework-configinfrastructure_stats.csv) " + + "--supported-api-list-file $(location framework-configinfrastructure_apis.csv) " + + "--gen-keep-all-file $(location framework-configinfrastructure_keep_all.txt) " + + "--gen-input-dump-file $(location framework-configinfrastructure_dump.txt) " + + + "--out-impl-jar $(location ravenwood.jar) " + + "--in-jar $(location :framework-configinfrastructure.impl{.jar}) " + + + "--policy-override-file $(location :ravenwood-common-policies) " + + "--policy-override-file $(location :framework-configinfrastructure-ravenwood-policies) ", + srcs: [ + ":framework-configinfrastructure.impl{.jar}", + + ":ravenwood-common-policies", + ":framework-configinfrastructure-ravenwood-policies", + ":ravenwood-standard-options", + ], + out: [ + "ravenwood.jar", + + // Following files are created just as FYI. + "framework-configinfrastructure_keep_all.txt", + "framework-configinfrastructure_dump.txt", + + "framework-configinfrastructure.log", + "framework-configinfrastructure_stats.csv", + "framework-configinfrastructure_apis.csv", + ], + visibility: ["//visibility:private"], +} + +java_genrule { + name: "framework-configinfrastructure.ravenwood", + defaults: ["ravenwood-internal-only-visibility-genrule"], + cmd: "cp $(in) $(out)", + srcs: [ + ":framework-configinfrastructure.ravenwood-base{ravenwood.jar}", + ], + out: [ + "framework-configinfrastructure.ravenwood.jar", + ], +} diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java index afa7a6c51abb..e2d73d1f1cb5 100644 --- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java +++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java @@ -40,6 +40,7 @@ import android.os.HandlerThread; import android.os.Looper; import android.os.ServiceManager; import android.os.SystemProperties; +import android.provider.DeviceConfig_host; import android.system.ErrnoException; import android.system.Os; import android.util.Log; @@ -333,6 +334,8 @@ public class RavenwoodRuntimeEnvironmentController { } android.os.Process.reset$ravenwood(); + DeviceConfig_host.reset(); + try { ResourcesManager.setInstance(null); // Better structure needed. } catch (Exception e) { diff --git a/ravenwood/runtime-helper-src/framework/android/provider/DeviceConfig_host.java b/ravenwood/runtime-helper-src/framework/android/provider/DeviceConfig_host.java new file mode 100644 index 000000000000..9c2188fd377a --- /dev/null +++ b/ravenwood/runtime-helper-src/framework/android/provider/DeviceConfig_host.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2024 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 android.provider; + +public class DeviceConfig_host { + + /** + * Called by Ravenwood runtime to reset all local changes. + */ + public static void reset() { + RavenwoodConfigDataStore.getInstance().clearAll(); + } + + /** + * Called by {@link DeviceConfig#newDataStore()} + */ + public static DeviceConfigDataStore newDataStore() { + return RavenwoodConfigDataStore.getInstance(); + } +} diff --git a/ravenwood/runtime-helper-src/framework/android/provider/RavenwoodConfigDataStore.java b/ravenwood/runtime-helper-src/framework/android/provider/RavenwoodConfigDataStore.java new file mode 100644 index 000000000000..4bc3de79fe6b --- /dev/null +++ b/ravenwood/runtime-helper-src/framework/android/provider/RavenwoodConfigDataStore.java @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2024 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 android.provider; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.ContentResolver; +import android.database.ContentObserver; +import android.net.Uri; +import android.os.Handler; +import android.os.Looper; +import android.provider.DeviceConfig.BadConfigException; +import android.provider.DeviceConfig.MonitorCallback; +import android.provider.DeviceConfig.Properties; + +import com.android.internal.annotations.GuardedBy; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.Executor; + +/** + * {@link DeviceConfigDataStore} used only on Ravenwood. + * + * TODO(b/368591527) Support monitor callback related features + * TODO(b/368591527) Support "default" related features + */ +final class RavenwoodConfigDataStore implements DeviceConfigDataStore { + private static final RavenwoodConfigDataStore sInstance = new RavenwoodConfigDataStore(); + + private final Object mLock = new Object(); + + @GuardedBy("mLock") + private int mSyncDisabledMode = DeviceConfig.SYNC_DISABLED_MODE_NONE; + + @GuardedBy("mLock") + private final HashMap<String, HashMap<String, String>> mStore = new HashMap<>(); + + private record ObserverInfo(String namespace, ContentObserver observer) { + } + + @GuardedBy("mLock") + private final ArrayList<ObserverInfo> mObservers = new ArrayList<>(); + + static RavenwoodConfigDataStore getInstance() { + return sInstance; + } + + private static void shouldNotBeCalled() { + throw new RuntimeException("shouldNotBeCalled"); + } + + void clearAll() { + synchronized (mLock) { + mSyncDisabledMode = DeviceConfig.SYNC_DISABLED_MODE_NONE; + mStore.clear(); + } + } + + @GuardedBy("mLock") + private HashMap<String, String> getNamespaceLocked(@NonNull String namespace) { + Objects.requireNonNull(namespace); + return mStore.computeIfAbsent(namespace, k -> new HashMap<>()); + } + + /** {@inheritDoc} */ + @NonNull + @Override + public Map<String, String> getAllProperties() { + synchronized (mLock) { + var ret = new HashMap<String, String>(); + + for (var namespaceAndMap : mStore.entrySet()) { + for (var nameAndValue : namespaceAndMap.getValue().entrySet()) { + ret.put(namespaceAndMap.getKey() + "/" + nameAndValue.getKey(), + nameAndValue.getValue()); + } + } + return ret; + } + } + + /** {@inheritDoc} */ + @NonNull + @Override + public Properties getProperties(@NonNull String namespace, @NonNull String... names) { + Objects.requireNonNull(namespace); + + synchronized (mLock) { + var namespaceMap = getNamespaceLocked(namespace); + + if (names.length == 0) { + return new Properties(namespace, Map.copyOf(namespaceMap)); + } else { + var map = new HashMap<String, String>(); + for (var name : names) { + Objects.requireNonNull(name); + map.put(name, namespaceMap.get(name)); + } + return new Properties(namespace, map); + } + } + } + + /** {@inheritDoc} */ + @Override + public boolean setProperties(@NonNull Properties properties) throws BadConfigException { + Objects.requireNonNull(properties); + + synchronized (mLock) { + var namespaceMap = getNamespaceLocked(properties.getNamespace()); + for (var kv : properties.getPropertyValues().entrySet()) { + namespaceMap.put( + Objects.requireNonNull(kv.getKey()), + Objects.requireNonNull(kv.getValue()) + ); + } + notifyObserversLock(properties.getNamespace(), + properties.getKeyset().toArray(new String[0])); + } + return true; + } + + /** {@inheritDoc} */ + @Override + public boolean setProperty(@NonNull String namespace, @NonNull String name, + @Nullable String value, boolean makeDefault) { + Objects.requireNonNull(namespace); + Objects.requireNonNull(name); + + synchronized (mLock) { + var namespaceMap = getNamespaceLocked(namespace); + namespaceMap.put(name, value); + + // makeDefault not supported. + notifyObserversLock(namespace, new String[]{name}); + } + return true; + } + + /** {@inheritDoc} */ + @Override + public boolean deleteProperty(@NonNull String namespace, @NonNull String name) { + Objects.requireNonNull(namespace); + Objects.requireNonNull(name); + + synchronized (mLock) { + var namespaceMap = getNamespaceLocked(namespace); + if (namespaceMap.remove(name) != null) { + notifyObserversLock(namespace, new String[]{name}); + } + } + return true; + } + + /** {@inheritDoc} */ + @Override + public void resetToDefaults(int resetMode, @Nullable String namespace) { + // not supported in DeviceConfig.java + shouldNotBeCalled(); + } + + /** {@inheritDoc} */ + @Override + public void setSyncDisabledMode(int syncDisabledMode) { + synchronized (mLock) { + mSyncDisabledMode = syncDisabledMode; + } + } + + /** {@inheritDoc} */ + @Override + public int getSyncDisabledMode() { + synchronized (mLock) { + return mSyncDisabledMode; + } + } + + /** {@inheritDoc} */ + @Override + public void setMonitorCallback(@NonNull ContentResolver resolver, @NonNull Executor executor, + @NonNull MonitorCallback callback) { + // not supported in DeviceConfig.java + shouldNotBeCalled(); + } + + /** {@inheritDoc} */ + @Override + public void clearMonitorCallback(@NonNull ContentResolver resolver) { + // not supported in DeviceConfig.java + shouldNotBeCalled(); + } + + /** {@inheritDoc} */ + @Override + public void registerContentObserver(@NonNull String namespace, boolean notifyForescendants, + ContentObserver contentObserver) { + synchronized (mLock) { + mObservers.add(new ObserverInfo( + Objects.requireNonNull(namespace), + Objects.requireNonNull(contentObserver) + )); + } + } + + /** {@inheritDoc} */ + @Override + public void unregisterContentObserver(@NonNull ContentObserver contentObserver) { + synchronized (mLock) { + for (int i = mObservers.size() - 1; i >= 0; i--) { + if (mObservers.get(i).observer == contentObserver) { + mObservers.remove(i); + } + } + } + } + + private static final Uri CONTENT_URI = Uri.parse("content://settings/config"); + + @GuardedBy("mLock") + private void notifyObserversLock(@NonNull String namespace, String[] keys) { + var urib = CONTENT_URI.buildUpon().appendPath(namespace); + for (var key : keys) { + urib.appendEncodedPath(key); + } + var uri = urib.build(); + + final var copy = List.copyOf(mObservers); + new Handler(Looper.getMainLooper()).post(() -> { + for (int i = copy.size() - 1; i >= 0; i--) { + if (copy.get(i).namespace.equals(namespace)) { + copy.get(i).observer.dispatchChange(false, uri); + } + } + }); + } +} |