blob: f0ba81b22a2930896572e597048c8bdb9e9b86a7 [file] [log] [blame]
/*
* 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 com.android.adservices.service.ui.data;
import static com.android.adservices.service.ui.ux.collection.PrivacySandboxUxCollection.UNSUPPORTED_UX;
import android.adservices.common.AdServicesStates;
import android.annotation.NonNull;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;
import androidx.annotation.RequiresApi;
import com.android.adservices.LogUtil;
import com.android.adservices.service.Flags;
import com.android.adservices.service.FlagsFactory;
import com.android.adservices.service.consent.AdServicesApiType;
import com.android.adservices.service.consent.ConsentManager;
import com.android.adservices.service.consent.DeviceRegionProvider;
import com.android.adservices.service.ui.enrollment.collection.PrivacySandboxEnrollmentChannelCollection;
import com.android.adservices.service.ui.ux.collection.PrivacySandboxUxCollection;
import java.util.Map;
/**
* Manager that deals with all UX related states. All other UX code should use this class to read ux
* component states. Specifically, this class:
* <li>Reads process statble UX flags from {@code Flags}, and provide these flags through the
* getFlags API.
* <li>Reads process statble consent manager bits such as UX and enrollment channel, so that these
* values are process stable.
*/
@RequiresApi(Build.VERSION_CODES.S)
public class UxStatesManager {
private static final Object LOCK = new Object();
private static volatile UxStatesManager sUxStatesManager;
private final Map<String, Boolean> mUxFlags;
private final ConsentManager mConsentManager;
private final SharedPreferences mUxSharedPreferences;
private PrivacySandboxUxCollection mUx;
private PrivacySandboxEnrollmentChannelCollection mEnrollmentChannel;
private final boolean mIsEeaDevice;
UxStatesManager(
@NonNull Context context,
@NonNull Flags flags,
@NonNull ConsentManager consentManager) {
mUxFlags = flags.getUxFlags();
mConsentManager = consentManager;
mIsEeaDevice = DeviceRegionProvider.isEuDevice(context);
mUxSharedPreferences =
context.getSharedPreferences("UX_SHARED_PREFERENCES", Context.MODE_PRIVATE);
}
/** Returns an instance of the UxStatesManager. */
@NonNull
public static UxStatesManager getInstance(Context context) {
if (sUxStatesManager == null) {
synchronized (LOCK) {
if (sUxStatesManager == null) {
sUxStatesManager =
new UxStatesManager(
context,
FlagsFactory.getFlags(),
ConsentManager.getInstance(context));
}
}
}
return sUxStatesManager;
}
/** Saves the AdServices states into data stores. */
public void persistAdServicesStates(AdServicesStates adServicesStates) {
// Only a subset of states should be persisted.
mConsentManager.setAdIdEnabled(adServicesStates.isAdIdEnabled());
mConsentManager.setU18Account(adServicesStates.isU18Account());
mConsentManager.setAdultAccount(adServicesStates.isAdultAccount());
mConsentManager.setEntryPointEnabled(adServicesStates.isPrivacySandboxUiEnabled());
}
/** Returns process statble UX flags. */
public boolean getFlag(String uxFlagKey) {
if (!mUxFlags.containsKey(uxFlagKey)) {
LogUtil.e("Key not found in cached UX flags: ", uxFlagKey);
}
Boolean value = mUxFlags.get(uxFlagKey);
return value != null ? value : false;
}
/** Returns process statble UX. */
public PrivacySandboxUxCollection getUx() {
// Lazy read.
if (mUx == null) {
mUx = mConsentManager.getUx();
}
return mUx != null ? mUx : UNSUPPORTED_UX;
}
/** Returns process statble enrollment channel. */
public PrivacySandboxEnrollmentChannelCollection getEnrollmentChannel() {
// Lazy read.
if (mEnrollmentChannel == null) {
mEnrollmentChannel = mConsentManager.getEnrollmentChannel(mUx);
}
return mEnrollmentChannel;
}
/** Returns process statble devicce region. */
public boolean isEeaDevice() {
return mIsEeaDevice;
}
/** Returns a common shared preference for storing temporary UX states. */
public SharedPreferences getUxSharedPreferences() {
return mUxSharedPreferences;
}
/**
* Returns whether the user is already enrolled for the current UX. or it is supervised account,
* we then set ux and default measurement consent.
*/
public boolean isEnrolledUser(Context context) {
boolean isNotificationDisplayed =
mConsentManager.wasGaUxNotificationDisplayed()
|| mConsentManager.wasU18NotificationDisplayed()
|| mConsentManager.wasNotificationDisplayed();
// We follow the Chrome's capabilities practice here, when user is not in adult account and
// u18 account, (the u18 account is for teen and un-supervised account), we are consider
// them as supervised accounts for now, it actually also contains robot account, but we
// don't have a capability for that, we will update this when we have the new capability.
// TODO: when new capability is available, update with new capability.
boolean isSupervisedUser =
!mConsentManager.isU18Account() && !mConsentManager.isAdultAccount();
// In case supervised account logging in second time and not able to set the ux to u18
if (isSupervisedUser) {
LogUtil.d("supervised user get");
mConsentManager.setUx(PrivacySandboxUxCollection.U18_UX);
}
if (!isNotificationDisplayed) {
if (isSupervisedUser) {
// We initial the default consent and notification.
LogUtil.d("supervised user initial");
mConsentManager.setU18NotificationDisplayed(true);
mConsentManager.enable(context, AdServicesApiType.MEASUREMENTS);
return true;
}
return false;
}
return true;
}
}