1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.adservices.ondevicepersonalization; 18 19 import static android.adservices.ondevicepersonalization.OnDevicePersonalizationPermissions.MODIFY_ONDEVICEPERSONALIZATION_STATE; 20 21 import android.adservices.ondevicepersonalization.aidl.IOnDevicePersonalizationConfigService; 22 import android.adservices.ondevicepersonalization.aidl.IOnDevicePersonalizationConfigServiceCallback; 23 import android.annotation.CallbackExecutor; 24 import android.annotation.FlaggedApi; 25 import android.annotation.NonNull; 26 import android.annotation.RequiresPermission; 27 import android.annotation.SystemApi; 28 import android.content.Context; 29 import android.os.Binder; 30 import android.os.OutcomeReceiver; 31 32 import com.android.adservices.ondevicepersonalization.flags.Flags; 33 import com.android.federatedcompute.internal.util.AbstractServiceBinder; 34 import com.android.internal.annotations.VisibleForTesting; 35 import com.android.ondevicepersonalization.internal.util.LoggerFactory; 36 37 import java.util.List; 38 import java.util.concurrent.CountDownLatch; 39 import java.util.concurrent.Executor; 40 41 /** 42 * OnDevicePersonalizationConfigManager provides system APIs 43 * for privileged APKs to control OnDevicePersonalization's enablement status. 44 * 45 * @hide 46 */ 47 @SystemApi 48 @FlaggedApi(Flags.FLAG_ON_DEVICE_PERSONALIZATION_APIS_ENABLED) 49 public class OnDevicePersonalizationConfigManager { 50 /** @hide */ 51 public static final String ON_DEVICE_PERSONALIZATION_CONFIG_SERVICE = 52 "on_device_personalization_config_service"; 53 private static final LoggerFactory.Logger sLogger = LoggerFactory.getLogger(); 54 private static final String TAG = OnDevicePersonalizationConfigManager.class.getSimpleName(); 55 56 private static final String ODP_CONFIG_SERVICE_PACKAGE_SUFFIX = 57 "com.android.ondevicepersonalization.services"; 58 59 private static final String ALT_ODP_CONFIG_SERVICE_PACKAGE_SUFFIX = 60 "com.google.android.ondevicepersonalization.services"; 61 private static final String ODP_CONFIG_SERVICE_INTENT = 62 "android.OnDevicePersonalizationConfigService"; 63 64 private final AbstractServiceBinder<IOnDevicePersonalizationConfigService> mServiceBinder; 65 66 /** @hide */ OnDevicePersonalizationConfigManager(@onNull Context context)67 public OnDevicePersonalizationConfigManager(@NonNull Context context) { 68 this( 69 AbstractServiceBinder.getServiceBinderByIntent( 70 context, 71 ODP_CONFIG_SERVICE_INTENT, 72 List.of( 73 ODP_CONFIG_SERVICE_PACKAGE_SUFFIX, 74 ALT_ODP_CONFIG_SERVICE_PACKAGE_SUFFIX), 75 IOnDevicePersonalizationConfigService.Stub::asInterface)); 76 } 77 78 /** @hide */ 79 @VisibleForTesting OnDevicePersonalizationConfigManager( AbstractServiceBinder<IOnDevicePersonalizationConfigService> serviceBinder)80 public OnDevicePersonalizationConfigManager( 81 AbstractServiceBinder<IOnDevicePersonalizationConfigService> serviceBinder) { 82 this.mServiceBinder = serviceBinder; 83 } 84 85 /** 86 * API users are expected to call this to modify personalization status for 87 * On Device Personalization. The status is persisted both in memory and to the disk. 88 * When reboot, the in-memory status will be restored from the disk. 89 * Personalization is disabled by default. 90 * 91 * @param enabled boolean whether On Device Personalization should be enabled. 92 * @param executor The {@link Executor} on which to invoke the callback. 93 * @param receiver This either returns null on success or {@link Exception} on failure. 94 * 95 * In case of an error, the receiver returns one of the following exceptions: 96 * Returns an {@link IllegalStateException} if the callback is unable to send back results. 97 * Returns a {@link SecurityException} if the caller is unauthorized to modify 98 * personalization status. 99 */ 100 @RequiresPermission(MODIFY_ONDEVICEPERSONALIZATION_STATE) setPersonalizationEnabled(boolean enabled, @NonNull @CallbackExecutor Executor executor, @NonNull OutcomeReceiver<Void, Exception> receiver)101 public void setPersonalizationEnabled(boolean enabled, 102 @NonNull @CallbackExecutor Executor executor, 103 @NonNull OutcomeReceiver<Void, Exception> receiver) { 104 CountDownLatch latch = new CountDownLatch(1); 105 try { 106 IOnDevicePersonalizationConfigService service = mServiceBinder.getService(executor); 107 service.setPersonalizationStatus(enabled, 108 new IOnDevicePersonalizationConfigServiceCallback.Stub() { 109 @Override 110 public void onSuccess() { 111 final long token = Binder.clearCallingIdentity(); 112 try { 113 executor.execute(() -> { 114 receiver.onResult(null); 115 latch.countDown(); 116 }); 117 } finally { 118 Binder.restoreCallingIdentity(token); 119 } 120 } 121 122 @Override 123 public void onFailure(int errorCode) { 124 final long token = Binder.clearCallingIdentity(); 125 try { 126 executor.execute(() -> { 127 sLogger.w(TAG + ": Unexpected failure from ODP" 128 + "config service with error code: " + errorCode); 129 receiver.onError( 130 new IllegalStateException("Unexpected failure.")); 131 latch.countDown(); 132 }); 133 } finally { 134 Binder.restoreCallingIdentity(token); 135 } 136 } 137 }); 138 } catch (IllegalArgumentException | NullPointerException e) { 139 latch.countDown(); 140 throw e; 141 } catch (SecurityException e) { 142 sLogger.w(TAG + ": Unauthorized call to ODP config service."); 143 receiver.onError(e); 144 latch.countDown(); 145 } catch (Exception e) { 146 sLogger.w(TAG + ": Unexpected exception during call to ODP config service."); 147 receiver.onError(e); 148 latch.countDown(); 149 } finally { 150 try { 151 latch.await(); 152 } catch (InterruptedException e) { 153 sLogger.e(TAG + ": Failed to set personalization.", e); 154 receiver.onError(e); 155 } 156 unbindFromService(); 157 } 158 } 159 160 /** 161 * Unbind from config service. 162 */ unbindFromService()163 private void unbindFromService() { 164 mServiceBinder.unbindFromService(); 165 } 166 } 167