diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index 517102741117f..5406333d855b3 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -462,6 +462,25 @@ package android.provider { method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public static java.util.Map> queryRawContactEntity(@NonNull android.content.ContentResolver, long); } + public class DeviceConfigInitializer { + method @Nullable public static android.provider.DeviceConfigServiceManager getDeviceConfigServiceManager(); + method public static void setDeviceConfigServiceManager(@NonNull android.provider.DeviceConfigServiceManager); + } + + public class DeviceConfigServiceManager { + method @NonNull public android.provider.DeviceConfigServiceManager.ServiceRegisterer getDeviceConfigUpdatableServiceRegisterer(); + } + + public static class DeviceConfigServiceManager.ServiceNotFoundException extends java.lang.Exception { + } + + public static final class DeviceConfigServiceManager.ServiceRegisterer { + method @Nullable public android.os.IBinder get(); + method @NonNull public android.os.IBinder getOrThrow() throws android.provider.DeviceConfigServiceManager.ServiceNotFoundException; + method public void register(@NonNull android.os.IBinder); + method @Nullable public android.os.IBinder tryGet(); + } + public final class Settings { field public static final int RESET_MODE_PACKAGE_DEFAULTS = 1; // 0x1 field public static final int RESET_MODE_TRUSTED_DEFAULTS = 4; // 0x4 diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index f78f45244ec55..543c5e700e332 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -151,6 +151,8 @@ import android.provider.BlockedNumberContract; import android.provider.CalendarContract; import android.provider.CallLog; import android.provider.ContactsContract; +import android.provider.DeviceConfigInitializer; +import android.provider.DeviceConfigServiceManager; import android.provider.Downloads; import android.provider.FontsContract; import android.provider.Settings; @@ -8139,8 +8141,11 @@ public final class ActivityThread extends ClientTransactionHandler MediaFrameworkInitializer.setMediaServiceManager(new MediaServiceManager()); BluetoothFrameworkInitializer.setBluetoothServiceManager(new BluetoothServiceManager()); BluetoothFrameworkInitializer.setBinderCallsStatsInitializer(context -> { - BinderCallsStats.startForBluetooth(context); }); + BinderCallsStats.startForBluetooth(context); + }); NfcFrameworkInitializer.setNfcServiceManager(new NfcServiceManager()); + + DeviceConfigInitializer.setDeviceConfigServiceManager(new DeviceConfigServiceManager()); } private void purgePendingResources() { diff --git a/core/java/android/provider/DeviceConfigInitializer.java b/core/java/android/provider/DeviceConfigInitializer.java new file mode 100644 index 0000000000000..d60449f800d5f --- /dev/null +++ b/core/java/android/provider/DeviceConfigInitializer.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2023 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.annotation.SystemApi; + +import java.util.Objects; + +/** + * Class that will hold an instance of {@link DeviceConfigServiceManager} + * which is used by {@link DeviceConfig} to retrieve an instance of the service. + * + * @hide + */ +@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) +public class DeviceConfigInitializer { + private static DeviceConfigServiceManager sDeviceConfigServiceManager; + + private static final Object sLock = new Object(); + + private DeviceConfigInitializer() { + // fully static class + } + + /** + * Setter for {@link DeviceConfigServiceManager}. Should be called only once. + * + */ + public static void setDeviceConfigServiceManager( + @NonNull DeviceConfigServiceManager serviceManager) { + synchronized (sLock) { + if (sDeviceConfigServiceManager != null) { + throw new IllegalStateException("setDeviceConfigServiceManager called twice!"); + } + Objects.requireNonNull(serviceManager, "serviceManager must not be null"); + + sDeviceConfigServiceManager = serviceManager; + } + } + + /** + * Getter for {@link DeviceConfigServiceManager}. + * + */ + @Nullable + public static DeviceConfigServiceManager getDeviceConfigServiceManager() { + synchronized (sLock) { + return sDeviceConfigServiceManager; + } + } +} diff --git a/core/java/android/provider/DeviceConfigServiceManager.java b/core/java/android/provider/DeviceConfigServiceManager.java new file mode 100644 index 0000000000000..c362c37954c05 --- /dev/null +++ b/core/java/android/provider/DeviceConfigServiceManager.java @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2023 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.annotation.SystemApi; +import android.os.IBinder; +import android.os.ServiceManager; + +/** + * Service Manager for the {@code android.provider.DeviceConfig} service. + * + *

Used to be able to get an instance of the service in places that don't have access to a + * {@code Context} + * + * @hide + */ +@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) +public class DeviceConfigServiceManager { + + /** + * @hide + */ + public DeviceConfigServiceManager() { + } + + /** + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public static final class ServiceRegisterer { + private final String mServiceName; + + /** + * @hide + */ + public ServiceRegisterer(String serviceName) { + mServiceName = serviceName; + } + + /** + * Register a system server binding object for a service. + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public void register(@NonNull IBinder service) { + ServiceManager.addService(mServiceName, service); + } + + /** + * Get the system server binding object for a service. + * + *

This blocks until the service instance is ready, + * or a timeout happens, in which case it returns null. + * + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @Nullable + public IBinder get() { + return ServiceManager.getService(mServiceName); + } + + /** + * Get the system server binding object for a service. + * + *

This blocks until the service instance is ready, + * or a timeout happens, in which case it throws {@link ServiceNotFoundException}. + * + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @NonNull + public IBinder getOrThrow() throws ServiceNotFoundException { + try { + return ServiceManager.getServiceOrThrow(mServiceName); + } catch (ServiceManager.ServiceNotFoundException e) { + throw new ServiceNotFoundException(mServiceName); + } + } + + /** + * Get the system server binding object for a service. If the specified service is + * not available, it returns null. + * + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @Nullable + public IBinder tryGet() { + return ServiceManager.checkService(mServiceName); + } + + } + + /** + * See {@link ServiceRegisterer#getOrThrow}. + * + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public static class ServiceNotFoundException extends ServiceManager.ServiceNotFoundException { + /** + * Constructor. + * + * @param name the name of the binder service that cannot be found. + * + * @hide + */ + public ServiceNotFoundException(@NonNull String name) { + super(name); + } + } + + /** + * Returns {@link ServiceRegisterer} for the device config service that + * is updatable via mainline. + * + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @NonNull + public ServiceRegisterer getDeviceConfigUpdatableServiceRegisterer() { + return new ServiceRegisterer("device_config_updatable"); + } +} diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java index f83ebaacb8e63..720d6dda24b2f 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java @@ -26,16 +26,23 @@ import android.content.AttributionSource; import android.content.IContentProvider; import android.os.Binder; import android.os.Bundle; +import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ShellCallback; import android.os.ShellCommand; import android.provider.DeviceConfig; +import android.provider.DeviceConfigShellCommandHandler; import android.provider.Settings; import android.provider.Settings.Config.SyncDisabledMode; +import android.provider.UpdatableDeviceConfigServiceReadiness; + +import com.android.internal.util.FastPrintWriter; import java.io.FileDescriptor; +import java.io.FileOutputStream; +import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collections; @@ -56,8 +63,32 @@ public final class DeviceConfigService extends Binder { @Override public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, - String[] args, ShellCallback callback, ResultReceiver resultReceiver) { - (new MyShellCommand(mProvider)).exec(this, in, out, err, args, callback, resultReceiver); + String[] args, ShellCallback callback, ResultReceiver resultReceiver) + throws RemoteException { + if (UpdatableDeviceConfigServiceReadiness.shouldStartUpdatableService()) { + callUpdableDeviceConfigShellCommandHandler(in, out, err, args, resultReceiver); + } else { + (new MyShellCommand(mProvider)) + .exec(this, in, out, err, args, callback, resultReceiver); + } + } + + private void callUpdableDeviceConfigShellCommandHandler(FileDescriptor in, FileDescriptor out, + FileDescriptor err, String[] args, ResultReceiver resultReceiver) { + int result = -1; + try ( + ParcelFileDescriptor inPfd = ParcelFileDescriptor.dup(in); + ParcelFileDescriptor outPfd = ParcelFileDescriptor.dup(out); + ParcelFileDescriptor errPfd = ParcelFileDescriptor.dup(err)) { + result = DeviceConfigShellCommandHandler.handleShellCommand(inPfd, outPfd, errPfd, + args); + } catch (IOException e) { + PrintWriter pw = new FastPrintWriter(new FileOutputStream(err)); + pw.println("dup() failed: " + e.getMessage()); + pw.flush(); + } finally { + resultReceiver.send(result, null); + } } static final class MyShellCommand extends ShellCommand {