Loading ravenwood/Android.bp +4 −0 Original line number Diff line number Diff line Loading @@ -124,6 +124,7 @@ java_library { ], libs: [ "framework-minus-apex.ravenwood", "framework-configinfrastructure.ravenwood", "ravenwood-helper-libcore-runtime", ], sdk_version: "core_current", Loading Loading @@ -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", Loading ravenwood/Framework.bp +54 −0 Original line number Diff line number Diff line Loading @@ -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", ], } ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java +3 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) { Loading ravenwood/runtime-helper-src/framework/android/provider/DeviceConfig_host.java 0 → 100644 +33 −0 Original line number Diff line number Diff line /* * 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(); } } ravenwood/runtime-helper-src/framework/android/provider/RavenwoodConfigDataStore.java 0 → 100644 +253 −0 Original line number Diff line number Diff line /* * 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); } } }); } } Loading
ravenwood/Android.bp +4 −0 Original line number Diff line number Diff line Loading @@ -124,6 +124,7 @@ java_library { ], libs: [ "framework-minus-apex.ravenwood", "framework-configinfrastructure.ravenwood", "ravenwood-helper-libcore-runtime", ], sdk_version: "core_current", Loading Loading @@ -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", Loading
ravenwood/Framework.bp +54 −0 Original line number Diff line number Diff line Loading @@ -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", ], }
ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java +3 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) { Loading
ravenwood/runtime-helper-src/framework/android/provider/DeviceConfig_host.java 0 → 100644 +33 −0 Original line number Diff line number Diff line /* * 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(); } }
ravenwood/runtime-helper-src/framework/android/provider/RavenwoodConfigDataStore.java 0 → 100644 +253 −0 Original line number Diff line number Diff line /* * 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); } } }); } }