Loading core/api/current.txt +19 −0 Original line number Diff line number Diff line Loading @@ -42159,6 +42159,25 @@ package android.service.settings.preferences { method @NonNull public android.service.settings.preferences.SettingsPreferenceMetadata.Builder setWriteSensitivity(int); } @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public abstract class SettingsPreferenceService extends android.app.Service { ctor public SettingsPreferenceService(); method @NonNull public final android.os.IBinder onBind(@Nullable android.content.Intent); method public abstract void onGetAllPreferenceMetadata(@NonNull android.service.settings.preferences.MetadataRequest, @NonNull android.os.OutcomeReceiver<android.service.settings.preferences.MetadataResult,java.lang.Exception>); method public abstract void onGetPreferenceValue(@NonNull android.service.settings.preferences.GetValueRequest, @NonNull android.os.OutcomeReceiver<android.service.settings.preferences.GetValueResult,java.lang.Exception>); method public abstract void onSetPreferenceValue(@NonNull android.service.settings.preferences.SetValueRequest, @NonNull android.os.OutcomeReceiver<android.service.settings.preferences.SetValueResult,java.lang.Exception>); field public static final String ACTION_PREFERENCE_SERVICE = "android.service.settings.preferences.action.PREFERENCE_SERVICE"; } @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public class SettingsPreferenceServiceClient implements java.lang.AutoCloseable { ctor public SettingsPreferenceServiceClient(@NonNull android.content.Context, @NonNull String); method public void close(); method public void getAllPreferenceMetadata(@NonNull android.service.settings.preferences.MetadataRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.service.settings.preferences.MetadataResult,java.lang.Exception>); method public void getPreferenceValue(@NonNull android.service.settings.preferences.GetValueRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.service.settings.preferences.GetValueResult,java.lang.Exception>); method public void setPreferenceValue(@NonNull android.service.settings.preferences.SetValueRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.service.settings.preferences.SetValueResult,java.lang.Exception>); method public void start(); method public void stop(); } @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public final class SettingsPreferenceValue implements android.os.Parcelable { method public int describeContents(); method public boolean getBooleanValue(); core/api/test-current.txt +8 −0 Original line number Diff line number Diff line Loading @@ -3247,6 +3247,14 @@ package android.service.quicksettings { } package android.service.settings.preferences { @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public class SettingsPreferenceServiceClient implements java.lang.AutoCloseable { ctor public SettingsPreferenceServiceClient(@NonNull android.content.Context, @NonNull String, boolean, @Nullable android.content.ServiceConnection); } } package android.service.voice { public class AlwaysOnHotwordDetector implements android.service.voice.HotwordDetector { Loading core/java/android/service/settings/preferences/ISettingsPreferenceService.aidl 0 → 100644 +18 −0 Original line number Diff line number Diff line package android.service.settings.preferences; import android.service.settings.preferences.GetValueRequest; import android.service.settings.preferences.IGetValueCallback; import android.service.settings.preferences.IMetadataCallback; import android.service.settings.preferences.ISetValueCallback; import android.service.settings.preferences.MetadataRequest; import android.service.settings.preferences.SetValueRequest; /** @hide */ oneway interface ISettingsPreferenceService { @EnforcePermission("READ_SYSTEM_PREFERENCES") void getAllPreferenceMetadata(in MetadataRequest request, IMetadataCallback callback) = 1; @EnforcePermission("READ_SYSTEM_PREFERENCES") void getPreferenceValue(in GetValueRequest request, IGetValueCallback callback) = 2; @EnforcePermission(allOf = {"READ_SYSTEM_PREFERENCES", "WRITE_SYSTEM_PREFERENCES"}) void setPreferenceValue(in SetValueRequest request, ISetValueCallback callback) = 3; } core/java/android/service/settings/preferences/SettingsPreferenceService.java 0 → 100644 +201 −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.service.settings.preferences; import android.Manifest; import android.annotation.EnforcePermission; import android.annotation.FlaggedApi; import android.app.Service; import android.content.Intent; import android.content.pm.PackageManager; import android.os.IBinder; import android.os.OutcomeReceiver; import android.os.PermissionEnforcer; import android.os.RemoteException; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.settingslib.flags.Flags; /** * Base class for a service that exposes its settings preferences to external access. * <p>This class is to be implemented by apps that contribute to the Android Settings surface. * Access to this service is permission guarded by * {@link android.permission.READ_SYSTEM_PREFERENCES} for binding and reading, and guarded by both * {@link android.permission.READ_SYSTEM_PREFERENCES} and * {@link android.permission.WRITE_SYSTEM_PREFERENCES} for writing. An additional checks for access * control are the responsibility of the implementing class. * * <p>This implementation must correspond to an exported service declaration in the host app * AndroidManifest.xml as follows * <pre class="prettyprint"> * {@literal * <service * android:permission="android.permission.READ_SYSTEM_PREFERENCES" * android:exported="true"> * <intent-filter> * <action android:name="android.service.settings.preferences.action.PREFERENCE_SERVICE" /> * </intent-filter> * </service>} * </pre> * * <ul> * <li>It is recommended to expose the metadata for most, if not all, preferences within a * settings app, thus implementing {@link #onGetAllPreferenceMetadata}. * <li>Exposing preferences for read access of their values is up to the implementer, but any * exposed must be a subset of the preferences exposed in {@link #onGetAllPreferenceMetadata}. * To expose a preference for read access, the implementation will contain * {@link #onGetPreferenceValue}. * <li>Exposing a preference for write access of their values is up to the implementer, but should * be done so with extra care and consideration, both for security and privacy. These must also * be a subset of those exposed in {@link #onGetAllPreferenceMetadata}. To expose a preference for * write access, the implementation will contain {@link #onSetPreferenceValue}. * </ul> */ @FlaggedApi(Flags.FLAG_SETTINGS_CATALYST) public abstract class SettingsPreferenceService extends Service { /** * Intent Action corresponding to a {@link SettingsPreferenceService}. Note that any checks for * such services must be accompanied by a check to ensure the host is a system application. * Given an {@link android.content.pm.ApplicationInfo} you can check for * {@link android.content.pm.ApplicationInfo#FLAG_SYSTEM}, or when querying * {@link PackageManager#queryIntentServices} you can provide the flag * {@link PackageManager#MATCH_SYSTEM_ONLY}. */ public static final String ACTION_PREFERENCE_SERVICE = "android.service.settings.preferences.action.PREFERENCE_SERVICE"; /** @hide */ @NonNull @Override public final IBinder onBind(@Nullable Intent intent) { return new ISettingsPreferenceService.Stub( PermissionEnforcer.fromContext(getApplicationContext())) { @EnforcePermission(Manifest.permission.READ_SYSTEM_PREFERENCES) @Override public void getAllPreferenceMetadata(MetadataRequest request, IMetadataCallback callback) { getAllPreferenceMetadata_enforcePermission(); onGetAllPreferenceMetadata(request, new OutcomeReceiver<>() { @Override public void onResult(MetadataResult result) { try { callback.onSuccess(result); } catch (RemoteException e) { e.rethrowFromSystemServer(); } } @Override public void onError(@NonNull Exception error) { try { callback.onFailure(); } catch (RemoteException e) { e.rethrowFromSystemServer(); } } }); } @EnforcePermission(Manifest.permission.READ_SYSTEM_PREFERENCES) @Override public void getPreferenceValue(GetValueRequest request, IGetValueCallback callback) { getPreferenceValue_enforcePermission(); onGetPreferenceValue(request, new OutcomeReceiver<>() { @Override public void onResult(GetValueResult result) { try { callback.onSuccess(result); } catch (RemoteException e) { e.rethrowFromSystemServer(); } } @Override public void onError(@NonNull Exception error) { try { callback.onFailure(); } catch (RemoteException e) { e.rethrowFromSystemServer(); } } }); } @EnforcePermission(allOf = { Manifest.permission.READ_SYSTEM_PREFERENCES, Manifest.permission.WRITE_SYSTEM_PREFERENCES }) @Override public void setPreferenceValue(SetValueRequest request, ISetValueCallback callback) { setPreferenceValue_enforcePermission(); onSetPreferenceValue(request, new OutcomeReceiver<>() { @Override public void onResult(SetValueResult result) { try { callback.onSuccess(result); } catch (RemoteException e) { e.rethrowFromSystemServer(); } } @Override public void onError(@NonNull Exception error) { try { callback.onFailure(); } catch (RemoteException e) { e.rethrowFromSystemServer(); } } }); } }; } /** * Retrieve the metadata for all exposed settings preferences within this application. This * data should be a snapshot of their state at the time of this method being called. * @param request object to specify request parameters * @param callback object to receive result or failure of request */ public abstract void onGetAllPreferenceMetadata( @NonNull MetadataRequest request, @NonNull OutcomeReceiver<MetadataResult, Exception> callback); /** * Retrieve the current value of the requested settings preference. If this value is not exposed * or cannot be obtained for some reason, the corresponding result code will be set on the * result object. * @param request object to specify request parameters * @param callback object to receive result or failure of request */ public abstract void onGetPreferenceValue( @NonNull GetValueRequest request, @NonNull OutcomeReceiver<GetValueResult, Exception> callback); /** * Set the value within the request to the target settings preference. If this value cannot * be written for some reason, the corresponding result code will be set on the result object. * @param request object to specify request parameters * @param callback object to receive result or failure of request */ public abstract void onSetPreferenceValue( @NonNull SetValueRequest request, @NonNull OutcomeReceiver<SetValueResult, Exception> callback); } core/java/android/service/settings/preferences/SettingsPreferenceServiceClient.java 0 → 100644 +248 −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.service.settings.preferences; import static android.service.settings.preferences.SettingsPreferenceService.ACTION_PREFERENCE_SERVICE; import android.annotation.CallbackExecutor; import android.annotation.FlaggedApi; import android.annotation.TestApi; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.os.IBinder; import android.os.OutcomeReceiver; import android.os.RemoteException; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.settingslib.flags.Flags; import java.util.List; import java.util.concurrent.Executor; /** * Client class responsible for binding to and interacting with an instance of * {@link SettingsPreferenceService}. * <p>This is a convenience class to handle the lifecycle of the service connection. * <p>This client will only interact with one instance at a time, * so if the caller requires multiple instances (multiple applications that provide settings), then * the caller must create multiple client classes, one for each instance required. To find all * available services, a caller may query {@link android.content.pm.PackageManager} for applications * that provide the intent action {@link SettingsPreferenceService#ACTION_PREFERENCE_SERVICE} that * are also system applications ({@link android.content.pm.ApplicationInfo#FLAG_SYSTEM}). */ @FlaggedApi(Flags.FLAG_SETTINGS_CATALYST) public class SettingsPreferenceServiceClient implements AutoCloseable { private final Context mContext; private final Intent mServiceIntent; private final ServiceConnection mServiceConnection; private final boolean mSystemOnly; private ISettingsPreferenceService mRemoteService; /** * Construct a client for binding to a {@link SettingsPreferenceService} provided by the * application corresponding to the provided package name. * @param packageName - package name for which this client will initiate a service binding */ public SettingsPreferenceServiceClient(@NonNull Context context, @NonNull String packageName) { this(context, packageName, true, null); } /** * @hide Only to be called directly by test */ @TestApi public SettingsPreferenceServiceClient(@NonNull Context context, @NonNull String packageName, boolean systemOnly, @Nullable ServiceConnection connectionListener) { mContext = context.getApplicationContext(); mServiceIntent = new Intent(ACTION_PREFERENCE_SERVICE).setPackage(packageName); mSystemOnly = systemOnly; mServiceConnection = createServiceConnection(connectionListener); } /** * Initiate binding to service. * <p>If no service exists for the package provided or the package is not for a system * application, no binding will occur. */ public void start() { PackageManager pm = mContext.getPackageManager(); PackageManager.ResolveInfoFlags flags; if (mSystemOnly) { flags = PackageManager.ResolveInfoFlags.of(PackageManager.MATCH_SYSTEM_ONLY); } else { flags = PackageManager.ResolveInfoFlags.of(PackageManager.MATCH_ALL); } List<ResolveInfo> infos = pm.queryIntentServices(mServiceIntent, flags); if (infos.size() == 1) { mContext.bindService(mServiceIntent, mServiceConnection, Context.BIND_AUTO_CREATE); } } /** * If there is an active service binding, unbind from that service. */ public void stop() { if (mRemoteService != null) { mRemoteService = null; mContext.unbindService(mServiceConnection); } } /** * Retrieve the metadata for all exposed settings preferences within the application. * @param request object to specify request parameters * @param executor {@link Executor} on which to invoke the receiver * @param receiver callback to receive the result or failure */ public void getAllPreferenceMetadata( @NonNull MetadataRequest request, @CallbackExecutor @NonNull Executor executor, @NonNull OutcomeReceiver<MetadataResult, Exception> receiver) { if (mRemoteService == null) { executor.execute(() -> receiver.onError(new IllegalStateException("Service not ready"))); return; } try { mRemoteService.getAllPreferenceMetadata(request, new IMetadataCallback.Stub() { @Override public void onSuccess(MetadataResult result) { executor.execute(() -> receiver.onResult(result)); } @Override public void onFailure() { executor.execute(() -> receiver.onError( new IllegalStateException("Service call failure"))); } }); } catch (RemoteException | RuntimeException e) { executor.execute(() -> receiver.onError(e)); } } /** * Retrieve the current value of the requested settings preference. * @param request object to specify request parameters * @param executor {@link Executor} on which to invoke the receiver * @param receiver callback to receive the result or failure */ public void getPreferenceValue(@NonNull GetValueRequest request, @CallbackExecutor @NonNull Executor executor, @NonNull OutcomeReceiver<GetValueResult, Exception> receiver) { if (mRemoteService == null) { executor.execute(() -> receiver.onError(new IllegalStateException("Service not ready"))); return; } try { mRemoteService.getPreferenceValue(request, new IGetValueCallback.Stub() { @Override public void onSuccess(GetValueResult result) { executor.execute(() -> receiver.onResult(result)); } @Override public void onFailure() { executor.execute(() -> receiver.onError( new IllegalStateException("Service call failure"))); } }); } catch (RemoteException | RuntimeException e) { executor.execute(() -> receiver.onError(e)); } } /** * Set the value on the target settings preference. * @param request object to specify request parameters * @param executor {@link Executor} on which to invoke the receiver * @param receiver callback to receive the result or failure */ public void setPreferenceValue(@NonNull SetValueRequest request, @CallbackExecutor @NonNull Executor executor, @NonNull OutcomeReceiver<SetValueResult, Exception> receiver) { if (mRemoteService == null) { executor.execute(() -> receiver.onError(new IllegalStateException("Service not ready"))); return; } try { mRemoteService.setPreferenceValue(request, new ISetValueCallback.Stub() { @Override public void onSuccess(SetValueResult result) { executor.execute(() -> receiver.onResult(result)); } @Override public void onFailure() { executor.execute(() -> receiver.onError( new IllegalStateException("Service call failure"))); } }); } catch (RemoteException | RuntimeException e) { executor.execute(() -> receiver.onError(e)); } } @NonNull private ServiceConnection createServiceConnection(@Nullable ServiceConnection listener) { return new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { mRemoteService = getPreferenceServiceInterface(service); if (listener != null) { listener.onServiceConnected(name, service); } } @Override public void onServiceDisconnected(ComponentName name) { mRemoteService = null; if (listener != null) { listener.onServiceDisconnected(name); } } }; } @NonNull private ISettingsPreferenceService getPreferenceServiceInterface(@NonNull IBinder service) { return ISettingsPreferenceService.Stub.asInterface(service); } /** * This client handles a resource, thus is it important to appropriately close that resource * when it is no longer needed. * <p>This method is provided by {@link AutoCloseable} and calling it * will unbind any service binding. */ @Override public void close() { stop(); } } Loading
core/api/current.txt +19 −0 Original line number Diff line number Diff line Loading @@ -42159,6 +42159,25 @@ package android.service.settings.preferences { method @NonNull public android.service.settings.preferences.SettingsPreferenceMetadata.Builder setWriteSensitivity(int); } @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public abstract class SettingsPreferenceService extends android.app.Service { ctor public SettingsPreferenceService(); method @NonNull public final android.os.IBinder onBind(@Nullable android.content.Intent); method public abstract void onGetAllPreferenceMetadata(@NonNull android.service.settings.preferences.MetadataRequest, @NonNull android.os.OutcomeReceiver<android.service.settings.preferences.MetadataResult,java.lang.Exception>); method public abstract void onGetPreferenceValue(@NonNull android.service.settings.preferences.GetValueRequest, @NonNull android.os.OutcomeReceiver<android.service.settings.preferences.GetValueResult,java.lang.Exception>); method public abstract void onSetPreferenceValue(@NonNull android.service.settings.preferences.SetValueRequest, @NonNull android.os.OutcomeReceiver<android.service.settings.preferences.SetValueResult,java.lang.Exception>); field public static final String ACTION_PREFERENCE_SERVICE = "android.service.settings.preferences.action.PREFERENCE_SERVICE"; } @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public class SettingsPreferenceServiceClient implements java.lang.AutoCloseable { ctor public SettingsPreferenceServiceClient(@NonNull android.content.Context, @NonNull String); method public void close(); method public void getAllPreferenceMetadata(@NonNull android.service.settings.preferences.MetadataRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.service.settings.preferences.MetadataResult,java.lang.Exception>); method public void getPreferenceValue(@NonNull android.service.settings.preferences.GetValueRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.service.settings.preferences.GetValueResult,java.lang.Exception>); method public void setPreferenceValue(@NonNull android.service.settings.preferences.SetValueRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.service.settings.preferences.SetValueResult,java.lang.Exception>); method public void start(); method public void stop(); } @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public final class SettingsPreferenceValue implements android.os.Parcelable { method public int describeContents(); method public boolean getBooleanValue();
core/api/test-current.txt +8 −0 Original line number Diff line number Diff line Loading @@ -3247,6 +3247,14 @@ package android.service.quicksettings { } package android.service.settings.preferences { @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public class SettingsPreferenceServiceClient implements java.lang.AutoCloseable { ctor public SettingsPreferenceServiceClient(@NonNull android.content.Context, @NonNull String, boolean, @Nullable android.content.ServiceConnection); } } package android.service.voice { public class AlwaysOnHotwordDetector implements android.service.voice.HotwordDetector { Loading
core/java/android/service/settings/preferences/ISettingsPreferenceService.aidl 0 → 100644 +18 −0 Original line number Diff line number Diff line package android.service.settings.preferences; import android.service.settings.preferences.GetValueRequest; import android.service.settings.preferences.IGetValueCallback; import android.service.settings.preferences.IMetadataCallback; import android.service.settings.preferences.ISetValueCallback; import android.service.settings.preferences.MetadataRequest; import android.service.settings.preferences.SetValueRequest; /** @hide */ oneway interface ISettingsPreferenceService { @EnforcePermission("READ_SYSTEM_PREFERENCES") void getAllPreferenceMetadata(in MetadataRequest request, IMetadataCallback callback) = 1; @EnforcePermission("READ_SYSTEM_PREFERENCES") void getPreferenceValue(in GetValueRequest request, IGetValueCallback callback) = 2; @EnforcePermission(allOf = {"READ_SYSTEM_PREFERENCES", "WRITE_SYSTEM_PREFERENCES"}) void setPreferenceValue(in SetValueRequest request, ISetValueCallback callback) = 3; }
core/java/android/service/settings/preferences/SettingsPreferenceService.java 0 → 100644 +201 −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.service.settings.preferences; import android.Manifest; import android.annotation.EnforcePermission; import android.annotation.FlaggedApi; import android.app.Service; import android.content.Intent; import android.content.pm.PackageManager; import android.os.IBinder; import android.os.OutcomeReceiver; import android.os.PermissionEnforcer; import android.os.RemoteException; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.settingslib.flags.Flags; /** * Base class for a service that exposes its settings preferences to external access. * <p>This class is to be implemented by apps that contribute to the Android Settings surface. * Access to this service is permission guarded by * {@link android.permission.READ_SYSTEM_PREFERENCES} for binding and reading, and guarded by both * {@link android.permission.READ_SYSTEM_PREFERENCES} and * {@link android.permission.WRITE_SYSTEM_PREFERENCES} for writing. An additional checks for access * control are the responsibility of the implementing class. * * <p>This implementation must correspond to an exported service declaration in the host app * AndroidManifest.xml as follows * <pre class="prettyprint"> * {@literal * <service * android:permission="android.permission.READ_SYSTEM_PREFERENCES" * android:exported="true"> * <intent-filter> * <action android:name="android.service.settings.preferences.action.PREFERENCE_SERVICE" /> * </intent-filter> * </service>} * </pre> * * <ul> * <li>It is recommended to expose the metadata for most, if not all, preferences within a * settings app, thus implementing {@link #onGetAllPreferenceMetadata}. * <li>Exposing preferences for read access of their values is up to the implementer, but any * exposed must be a subset of the preferences exposed in {@link #onGetAllPreferenceMetadata}. * To expose a preference for read access, the implementation will contain * {@link #onGetPreferenceValue}. * <li>Exposing a preference for write access of their values is up to the implementer, but should * be done so with extra care and consideration, both for security and privacy. These must also * be a subset of those exposed in {@link #onGetAllPreferenceMetadata}. To expose a preference for * write access, the implementation will contain {@link #onSetPreferenceValue}. * </ul> */ @FlaggedApi(Flags.FLAG_SETTINGS_CATALYST) public abstract class SettingsPreferenceService extends Service { /** * Intent Action corresponding to a {@link SettingsPreferenceService}. Note that any checks for * such services must be accompanied by a check to ensure the host is a system application. * Given an {@link android.content.pm.ApplicationInfo} you can check for * {@link android.content.pm.ApplicationInfo#FLAG_SYSTEM}, or when querying * {@link PackageManager#queryIntentServices} you can provide the flag * {@link PackageManager#MATCH_SYSTEM_ONLY}. */ public static final String ACTION_PREFERENCE_SERVICE = "android.service.settings.preferences.action.PREFERENCE_SERVICE"; /** @hide */ @NonNull @Override public final IBinder onBind(@Nullable Intent intent) { return new ISettingsPreferenceService.Stub( PermissionEnforcer.fromContext(getApplicationContext())) { @EnforcePermission(Manifest.permission.READ_SYSTEM_PREFERENCES) @Override public void getAllPreferenceMetadata(MetadataRequest request, IMetadataCallback callback) { getAllPreferenceMetadata_enforcePermission(); onGetAllPreferenceMetadata(request, new OutcomeReceiver<>() { @Override public void onResult(MetadataResult result) { try { callback.onSuccess(result); } catch (RemoteException e) { e.rethrowFromSystemServer(); } } @Override public void onError(@NonNull Exception error) { try { callback.onFailure(); } catch (RemoteException e) { e.rethrowFromSystemServer(); } } }); } @EnforcePermission(Manifest.permission.READ_SYSTEM_PREFERENCES) @Override public void getPreferenceValue(GetValueRequest request, IGetValueCallback callback) { getPreferenceValue_enforcePermission(); onGetPreferenceValue(request, new OutcomeReceiver<>() { @Override public void onResult(GetValueResult result) { try { callback.onSuccess(result); } catch (RemoteException e) { e.rethrowFromSystemServer(); } } @Override public void onError(@NonNull Exception error) { try { callback.onFailure(); } catch (RemoteException e) { e.rethrowFromSystemServer(); } } }); } @EnforcePermission(allOf = { Manifest.permission.READ_SYSTEM_PREFERENCES, Manifest.permission.WRITE_SYSTEM_PREFERENCES }) @Override public void setPreferenceValue(SetValueRequest request, ISetValueCallback callback) { setPreferenceValue_enforcePermission(); onSetPreferenceValue(request, new OutcomeReceiver<>() { @Override public void onResult(SetValueResult result) { try { callback.onSuccess(result); } catch (RemoteException e) { e.rethrowFromSystemServer(); } } @Override public void onError(@NonNull Exception error) { try { callback.onFailure(); } catch (RemoteException e) { e.rethrowFromSystemServer(); } } }); } }; } /** * Retrieve the metadata for all exposed settings preferences within this application. This * data should be a snapshot of their state at the time of this method being called. * @param request object to specify request parameters * @param callback object to receive result or failure of request */ public abstract void onGetAllPreferenceMetadata( @NonNull MetadataRequest request, @NonNull OutcomeReceiver<MetadataResult, Exception> callback); /** * Retrieve the current value of the requested settings preference. If this value is not exposed * or cannot be obtained for some reason, the corresponding result code will be set on the * result object. * @param request object to specify request parameters * @param callback object to receive result or failure of request */ public abstract void onGetPreferenceValue( @NonNull GetValueRequest request, @NonNull OutcomeReceiver<GetValueResult, Exception> callback); /** * Set the value within the request to the target settings preference. If this value cannot * be written for some reason, the corresponding result code will be set on the result object. * @param request object to specify request parameters * @param callback object to receive result or failure of request */ public abstract void onSetPreferenceValue( @NonNull SetValueRequest request, @NonNull OutcomeReceiver<SetValueResult, Exception> callback); }
core/java/android/service/settings/preferences/SettingsPreferenceServiceClient.java 0 → 100644 +248 −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.service.settings.preferences; import static android.service.settings.preferences.SettingsPreferenceService.ACTION_PREFERENCE_SERVICE; import android.annotation.CallbackExecutor; import android.annotation.FlaggedApi; import android.annotation.TestApi; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.os.IBinder; import android.os.OutcomeReceiver; import android.os.RemoteException; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.settingslib.flags.Flags; import java.util.List; import java.util.concurrent.Executor; /** * Client class responsible for binding to and interacting with an instance of * {@link SettingsPreferenceService}. * <p>This is a convenience class to handle the lifecycle of the service connection. * <p>This client will only interact with one instance at a time, * so if the caller requires multiple instances (multiple applications that provide settings), then * the caller must create multiple client classes, one for each instance required. To find all * available services, a caller may query {@link android.content.pm.PackageManager} for applications * that provide the intent action {@link SettingsPreferenceService#ACTION_PREFERENCE_SERVICE} that * are also system applications ({@link android.content.pm.ApplicationInfo#FLAG_SYSTEM}). */ @FlaggedApi(Flags.FLAG_SETTINGS_CATALYST) public class SettingsPreferenceServiceClient implements AutoCloseable { private final Context mContext; private final Intent mServiceIntent; private final ServiceConnection mServiceConnection; private final boolean mSystemOnly; private ISettingsPreferenceService mRemoteService; /** * Construct a client for binding to a {@link SettingsPreferenceService} provided by the * application corresponding to the provided package name. * @param packageName - package name for which this client will initiate a service binding */ public SettingsPreferenceServiceClient(@NonNull Context context, @NonNull String packageName) { this(context, packageName, true, null); } /** * @hide Only to be called directly by test */ @TestApi public SettingsPreferenceServiceClient(@NonNull Context context, @NonNull String packageName, boolean systemOnly, @Nullable ServiceConnection connectionListener) { mContext = context.getApplicationContext(); mServiceIntent = new Intent(ACTION_PREFERENCE_SERVICE).setPackage(packageName); mSystemOnly = systemOnly; mServiceConnection = createServiceConnection(connectionListener); } /** * Initiate binding to service. * <p>If no service exists for the package provided or the package is not for a system * application, no binding will occur. */ public void start() { PackageManager pm = mContext.getPackageManager(); PackageManager.ResolveInfoFlags flags; if (mSystemOnly) { flags = PackageManager.ResolveInfoFlags.of(PackageManager.MATCH_SYSTEM_ONLY); } else { flags = PackageManager.ResolveInfoFlags.of(PackageManager.MATCH_ALL); } List<ResolveInfo> infos = pm.queryIntentServices(mServiceIntent, flags); if (infos.size() == 1) { mContext.bindService(mServiceIntent, mServiceConnection, Context.BIND_AUTO_CREATE); } } /** * If there is an active service binding, unbind from that service. */ public void stop() { if (mRemoteService != null) { mRemoteService = null; mContext.unbindService(mServiceConnection); } } /** * Retrieve the metadata for all exposed settings preferences within the application. * @param request object to specify request parameters * @param executor {@link Executor} on which to invoke the receiver * @param receiver callback to receive the result or failure */ public void getAllPreferenceMetadata( @NonNull MetadataRequest request, @CallbackExecutor @NonNull Executor executor, @NonNull OutcomeReceiver<MetadataResult, Exception> receiver) { if (mRemoteService == null) { executor.execute(() -> receiver.onError(new IllegalStateException("Service not ready"))); return; } try { mRemoteService.getAllPreferenceMetadata(request, new IMetadataCallback.Stub() { @Override public void onSuccess(MetadataResult result) { executor.execute(() -> receiver.onResult(result)); } @Override public void onFailure() { executor.execute(() -> receiver.onError( new IllegalStateException("Service call failure"))); } }); } catch (RemoteException | RuntimeException e) { executor.execute(() -> receiver.onError(e)); } } /** * Retrieve the current value of the requested settings preference. * @param request object to specify request parameters * @param executor {@link Executor} on which to invoke the receiver * @param receiver callback to receive the result or failure */ public void getPreferenceValue(@NonNull GetValueRequest request, @CallbackExecutor @NonNull Executor executor, @NonNull OutcomeReceiver<GetValueResult, Exception> receiver) { if (mRemoteService == null) { executor.execute(() -> receiver.onError(new IllegalStateException("Service not ready"))); return; } try { mRemoteService.getPreferenceValue(request, new IGetValueCallback.Stub() { @Override public void onSuccess(GetValueResult result) { executor.execute(() -> receiver.onResult(result)); } @Override public void onFailure() { executor.execute(() -> receiver.onError( new IllegalStateException("Service call failure"))); } }); } catch (RemoteException | RuntimeException e) { executor.execute(() -> receiver.onError(e)); } } /** * Set the value on the target settings preference. * @param request object to specify request parameters * @param executor {@link Executor} on which to invoke the receiver * @param receiver callback to receive the result or failure */ public void setPreferenceValue(@NonNull SetValueRequest request, @CallbackExecutor @NonNull Executor executor, @NonNull OutcomeReceiver<SetValueResult, Exception> receiver) { if (mRemoteService == null) { executor.execute(() -> receiver.onError(new IllegalStateException("Service not ready"))); return; } try { mRemoteService.setPreferenceValue(request, new ISetValueCallback.Stub() { @Override public void onSuccess(SetValueResult result) { executor.execute(() -> receiver.onResult(result)); } @Override public void onFailure() { executor.execute(() -> receiver.onError( new IllegalStateException("Service call failure"))); } }); } catch (RemoteException | RuntimeException e) { executor.execute(() -> receiver.onError(e)); } } @NonNull private ServiceConnection createServiceConnection(@Nullable ServiceConnection listener) { return new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { mRemoteService = getPreferenceServiceInterface(service); if (listener != null) { listener.onServiceConnected(name, service); } } @Override public void onServiceDisconnected(ComponentName name) { mRemoteService = null; if (listener != null) { listener.onServiceDisconnected(name); } } }; } @NonNull private ISettingsPreferenceService getPreferenceServiceInterface(@NonNull IBinder service) { return ISettingsPreferenceService.Stub.asInterface(service); } /** * This client handles a resource, thus is it important to appropriately close that resource * when it is no longer needed. * <p>This method is provided by {@link AutoCloseable} and calling it * will unbind any service binding. */ @Override public void close() { stop(); } }