blob: a4c3bf0db76117baa40cd09ebab6031267c7e8ba [file] [log] [blame]
/*
* Copyright (C) 2022 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.server.adservices;
import static android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_MANAGER;
import android.adservices.common.AdServicesPermissions;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.app.adservices.AdServicesManager;
import android.app.adservices.IAdServicesManager;
import android.app.adservices.consent.ConsentParcel;
import android.app.adservices.topics.TopicParcel;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.VersionedPackage;
import android.content.rollback.PackageRollbackInfo;
import android.content.rollback.RollbackInfo;
import android.content.rollback.RollbackManager;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.util.ArrayMap;
import android.util.Dumpable;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalManagerRegistry;
import com.android.server.SystemService;
import com.android.server.adservices.data.topics.TopicsDao;
import com.android.server.adservices.feature.PrivacySandboxFeatureType;
import com.android.server.sdksandbox.SdkSandboxManagerLocal;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/** @hide */
// TODO(b/267667963): Offload methods from binder thread to background thread.
public class AdServicesManagerService extends IAdServicesManager.Stub {
// The base directory for AdServices System Service.
private static final String SYSTEM_DATA = "/data/system/";
public static String ADSERVICES_BASE_DIR = SYSTEM_DATA + "adservices";
private static final String ERROR_MESSAGE_NOT_PERMITTED_TO_CALL_ADSERVICESMANAGER_API =
"Unauthorized caller. Permission to call AdServicesManager API is not granted in System"
+ " Server.";
private final Object mRegisterReceiverLock = new Object();
private final Object mRollbackCheckLock = new Object();
private final Object mSetPackageVersionLock = new Object();
/**
* Broadcast send from the system service to the AdServices module when a package has been
* installed/uninstalled. This intent must match the intent defined in the AdServices manifest.
*/
private static final String PACKAGE_CHANGED_BROADCAST =
"com.android.adservices.PACKAGE_CHANGED";
/** Key for designating the specific action. */
private static final String ACTION_KEY = "action";
/** Value if the package change was an uninstallation. */
private static final String PACKAGE_FULLY_REMOVED = "package_fully_removed";
/** Value if the package change was an installation. */
private static final String PACKAGE_ADDED = "package_added";
/** Value if the package has its data cleared. */
private static final String PACKAGE_DATA_CLEARED = "package_data_cleared";
private final Context mContext;
@GuardedBy("mRegisterReceiverLock")
private BroadcastReceiver mSystemServicePackageChangedReceiver;
@GuardedBy("mRegisterReceiverLock")
private BroadcastReceiver mSystemServiceUserActionReceiver;
@GuardedBy("mRegisterReceiverLock")
private HandlerThread mHandlerThread;
@GuardedBy("mRegisterReceiverLock")
private Handler mHandler;
@GuardedBy("mSetPackageVersionLock")
private int mAdServicesModuleVersion;
@GuardedBy("mSetPackageVersionLock")
private String mAdServicesModuleName;
@GuardedBy("mRollbackCheckLock")
private final Map<Integer, VersionedPackage> mAdServicesPackagesRolledBackFrom =
new ArrayMap<>();
@GuardedBy("mRollbackCheckLock")
private final Map<Integer, VersionedPackage> mAdServicesPackagesRolledBackTo = new ArrayMap<>();
// This will be triggered when there is a flag change.
private final DeviceConfig.OnPropertiesChangedListener mOnFlagsChangedListener =
properties -> {
if (!properties.getNamespace().equals(DeviceConfig.NAMESPACE_ADSERVICES)) {
return;
}
registerReceivers();
setAdServicesApexVersion();
setRollbackStatus();
};
private final UserInstanceManager mUserInstanceManager;
@VisibleForTesting
AdServicesManagerService(Context context, UserInstanceManager userInstanceManager) {
mContext = context;
mUserInstanceManager = userInstanceManager;
DeviceConfig.addOnPropertiesChangedListener(
DeviceConfig.NAMESPACE_ADSERVICES,
mContext.getMainExecutor(),
mOnFlagsChangedListener);
registerReceivers();
setAdServicesApexVersion();
setRollbackStatus();
}
/** @hide */
public static final class Lifecycle extends SystemService implements Dumpable {
private final AdServicesManagerService mService;
/** @hide */
public Lifecycle(Context context) {
super(context);
TopicsDao topicsDao = TopicsDao.getInstance(context);
mService =
new AdServicesManagerService(
context, new UserInstanceManager(topicsDao, ADSERVICES_BASE_DIR));
LogUtil.d("AdServicesManagerService constructed!");
}
/** @hide */
@Override
public void onStart() {
LogUtil.d("AdServicesManagerService started!");
// TODO(b/262282035): Fix this work around in U+.
// TODO(b/263128170): Add cts-root tests to make sure that we can start the
// AdServicesManager in U+
// Register the AdServicesManagerService with the SdkSandboxManagerService.
// This is a workaround for b/262282035.
// This works since we start the SdkSandboxManagerService before the
// AdServicesManagerService in the SystemServer.java
SdkSandboxManagerLocal sdkSandboxManagerLocal =
LocalManagerRegistry.getManager(SdkSandboxManagerLocal.class);
if (sdkSandboxManagerLocal != null) {
sdkSandboxManagerLocal.registerAdServicesManagerService(mService);
} else {
throw new IllegalStateException(
"SdkSandboxManagerLocal not found when registering AdServicesManager!");
}
}
@Override
public String getDumpableName() {
return "AdServices";
}
@Override
public void dump(PrintWriter writer, String[] args) {
// Usage: adb shell dumpsys system_server_dumper --name AdServices
mService.dump(/* fd= */ null, writer, args);
}
}
@Override
@RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_MANAGER)
public ConsentParcel getConsent(@ConsentParcel.ConsentApiType int consentApiType) {
enforceAdServicesManagerPermission();
final int userIdentifier = getUserIdentifierFromBinderCallingUid();
LogUtil.v("getConsent() for User Identifier %d", userIdentifier);
try {
return mUserInstanceManager
.getOrCreateUserConsentManagerInstance(userIdentifier)
.getConsent(consentApiType);
} catch (IOException e) {
LogUtil.e(e, "Failed to getConsent with exception. Return REVOKED!");
return ConsentParcel.createRevokedConsent(consentApiType);
}
}
// Return the User Identifier from the CallingUid.
private int getUserIdentifierFromBinderCallingUid() {
return UserHandle.getUserHandleForUid(Binder.getCallingUid()).getIdentifier();
}
@Override
@RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_MANAGER)
public void setConsent(ConsentParcel consentParcel) {
enforceAdServicesManagerPermission();
Objects.requireNonNull(consentParcel);
final int userIdentifier = getUserIdentifierFromBinderCallingUid();
LogUtil.v("setConsent() for User Identifier %d", userIdentifier);
try {
mUserInstanceManager
.getOrCreateUserConsentManagerInstance(userIdentifier)
.setConsent(consentParcel);
} catch (IOException e) {
LogUtil.e(e, "Failed to persist the consent.");
}
}
@Override
@RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_MANAGER)
public void recordNotificationDisplayed() {
enforceAdServicesManagerPermission();
final int userIdentifier = getUserIdentifierFromBinderCallingUid();
LogUtil.v("recordNotificationDisplayed() for User Identifier %d", userIdentifier);
try {
mUserInstanceManager
.getOrCreateUserConsentManagerInstance(userIdentifier)
.recordNotificationDisplayed();
} catch (IOException e) {
LogUtil.e(e, "Failed to Record Notification Displayed.");
}
}
/**
* Record blocked topics.
*
* @param blockedTopicParcels the blocked topics to record
*/
@Override
@RequiresPermission(ACCESS_ADSERVICES_MANAGER)
public void recordBlockedTopic(@NonNull List<TopicParcel> blockedTopicParcels) {
enforceAdServicesManagerPermission();
final int userIdentifier = getUserIdentifierFromBinderCallingUid();
LogUtil.v("recordBlockedTopic() for User Identifier %d", userIdentifier);
mUserInstanceManager
.getOrCreateUserBlockedTopicsManagerInstance(userIdentifier)
.recordBlockedTopic(blockedTopicParcels);
}
/**
* Remove a blocked topic.
*
* @param blockedTopicParcel the blocked topic to remove
*/
@Override
@RequiresPermission(ACCESS_ADSERVICES_MANAGER)
public void removeBlockedTopic(@NonNull TopicParcel blockedTopicParcel) {
enforceAdServicesManagerPermission();
final int userIdentifier = getUserIdentifierFromBinderCallingUid();
LogUtil.v("removeBlockedTopic() for User Identifier %d", userIdentifier);
mUserInstanceManager
.getOrCreateUserBlockedTopicsManagerInstance(userIdentifier)
.removeBlockedTopic(blockedTopicParcel);
}
/**
* Get all blocked topics.
*
* @return a {@code List} of all blocked topics.
*/
@Override
@RequiresPermission(ACCESS_ADSERVICES_MANAGER)
public List<TopicParcel> retrieveAllBlockedTopics() {
enforceAdServicesManagerPermission();
final int userIdentifier = getUserIdentifierFromBinderCallingUid();
return mUserInstanceManager
.getOrCreateUserBlockedTopicsManagerInstance(userIdentifier)
.retrieveAllBlockedTopics();
}
/** Clear all Blocked Topics */
@Override
@RequiresPermission(ACCESS_ADSERVICES_MANAGER)
public void clearAllBlockedTopics() {
enforceAdServicesManagerPermission();
final int userIdentifier = getUserIdentifierFromBinderCallingUid();
LogUtil.v("clearAllBlockedTopics() for User Identifier %d", userIdentifier);
mUserInstanceManager
.getOrCreateUserBlockedTopicsManagerInstance(userIdentifier)
.clearAllBlockedTopics();
}
@Override
@RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_MANAGER)
public boolean wasNotificationDisplayed() {
enforceAdServicesManagerPermission();
final int userIdentifier = getUserIdentifierFromBinderCallingUid();
LogUtil.v("wasNotificationDisplayed() for User Identifier %d", userIdentifier);
try {
return mUserInstanceManager
.getOrCreateUserConsentManagerInstance(userIdentifier)
.wasNotificationDisplayed();
} catch (IOException e) {
LogUtil.e(e, "Failed to get the wasNotificationDisplayed.");
return false;
}
}
@Override
@RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_MANAGER)
public void recordGaUxNotificationDisplayed() {
enforceAdServicesManagerPermission();
final int userIdentifier = getUserIdentifierFromBinderCallingUid();
LogUtil.v("recordGaUxNotificationDisplayed() for User Identifier %d", userIdentifier);
try {
mUserInstanceManager
.getOrCreateUserConsentManagerInstance(userIdentifier)
.recordGaUxNotificationDisplayed();
} catch (IOException e) {
LogUtil.e(e, "Fail to Record GA UX Notification Displayed.");
}
}
@Override
@RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_MANAGER)
public void recordDefaultConsent(boolean defaultConsent) {
enforceAdServicesManagerPermission();
final int userIdentifier = getUserIdentifierFromBinderCallingUid();
LogUtil.v("recordDefaultConsent() for User Identifier %d", userIdentifier);
try {
mUserInstanceManager
.getOrCreateUserConsentManagerInstance(userIdentifier)
.recordDefaultConsent(defaultConsent);
} catch (IOException e) {
LogUtil.e(e, "Fail to record default consent: " + e.getMessage());
}
}
@Override
@RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_MANAGER)
public void recordTopicsDefaultConsent(boolean defaultConsent) {
enforceAdServicesManagerPermission();
final int userIdentifier = getUserIdentifierFromBinderCallingUid();
LogUtil.v("recordTopicsDefaultConsent() for User Identifier %d", userIdentifier);
try {
mUserInstanceManager
.getOrCreateUserConsentManagerInstance(userIdentifier)
.recordTopicsDefaultConsent(defaultConsent);
} catch (IOException e) {
LogUtil.e(e, "Fail to record topics default consent: " + e.getMessage());
}
}
@Override
@RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_MANAGER)
public void recordFledgeDefaultConsent(boolean defaultConsent) {
enforceAdServicesManagerPermission();
final int userIdentifier = getUserIdentifierFromBinderCallingUid();
LogUtil.v("recordFledgeDefaultConsent() for User Identifier %d", userIdentifier);
try {
mUserInstanceManager
.getOrCreateUserConsentManagerInstance(userIdentifier)
.recordFledgeDefaultConsent(defaultConsent);
} catch (IOException e) {
LogUtil.e(e, "Fail to record fledge default consent: " + e.getMessage());
}
}
@Override
@RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_MANAGER)
public void recordMeasurementDefaultConsent(boolean defaultConsent) {
enforceAdServicesManagerPermission();
final int userIdentifier = getUserIdentifierFromBinderCallingUid();
LogUtil.v("recordMeasurementDefaultConsent() for User Identifier %d", userIdentifier);
try {
mUserInstanceManager
.getOrCreateUserConsentManagerInstance(userIdentifier)
.recordMeasurementDefaultConsent(defaultConsent);
} catch (IOException e) {
LogUtil.e(e, "Fail to record measurement default consent: " + e.getMessage());
}
}
@Override
@RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_MANAGER)
public void recordDefaultAdIdState(boolean defaultAdIdState) {
enforceAdServicesManagerPermission();
final int userIdentifier = getUserIdentifierFromBinderCallingUid();
LogUtil.v("recordDefaultAdIdState() for User Identifier %d", userIdentifier);
try {
mUserInstanceManager
.getOrCreateUserConsentManagerInstance(userIdentifier)
.recordDefaultAdIdState(defaultAdIdState);
} catch (IOException e) {
LogUtil.e(e, "Fail to record default AdId state: " + e.getMessage());
}
}
@Override
@RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_MANAGER)
public void recordUserManualInteractionWithConsent(int interaction) {
enforceAdServicesManagerPermission();
final int userIdentifier = getUserIdentifierFromBinderCallingUid();
LogUtil.v(
"recordUserManualInteractionWithConsent() for User Identifier %d, interaction %d",
userIdentifier, interaction);
try {
mUserInstanceManager
.getOrCreateUserConsentManagerInstance(userIdentifier)
.recordUserManualInteractionWithConsent(interaction);
} catch (IOException e) {
LogUtil.e(
e, "Fail to record default manual interaction with consent: " + e.getMessage());
}
}
@Override
@RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_MANAGER)
public boolean getTopicsDefaultConsent() {
enforceAdServicesManagerPermission();
final int userIdentifier = getUserIdentifierFromBinderCallingUid();
LogUtil.v("getTopicsDefaultConsent() for User Identifier %d", userIdentifier);
try {
return mUserInstanceManager
.getOrCreateUserConsentManagerInstance(userIdentifier)
.getTopicsDefaultConsent();
} catch (IOException e) {
LogUtil.e(e, "Fail to get topics default consent.");
return false;
}
}
@Override
@RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_MANAGER)
public boolean getFledgeDefaultConsent() {
enforceAdServicesManagerPermission();
final int userIdentifier = getUserIdentifierFromBinderCallingUid();
LogUtil.v("getFledgeDefaultConsent() for User Identifier %d", userIdentifier);
try {
return mUserInstanceManager
.getOrCreateUserConsentManagerInstance(userIdentifier)
.getFledgeDefaultConsent();
} catch (IOException e) {
LogUtil.e(e, "Fail to get FLEDGE default consent.");
return false;
}
}
@Override
@RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_MANAGER)
public boolean getMeasurementDefaultConsent() {
enforceAdServicesManagerPermission();
final int userIdentifier = getUserIdentifierFromBinderCallingUid();
LogUtil.v("getMeasurementDefaultConsent() for User Identifier %d", userIdentifier);
try {
return mUserInstanceManager
.getOrCreateUserConsentManagerInstance(userIdentifier)
.getMeasurementDefaultConsent();
} catch (IOException e) {
LogUtil.e(e, "Fail to get measurement default consent.");
return false;
}
}
@Override
@RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_MANAGER)
public boolean getDefaultAdIdState() {
enforceAdServicesManagerPermission();
final int userIdentifier = getUserIdentifierFromBinderCallingUid();
LogUtil.v("getDefaultAdIdState() for User Identifier %d", userIdentifier);
try {
return mUserInstanceManager
.getOrCreateUserConsentManagerInstance(userIdentifier)
.getDefaultAdIdState();
} catch (IOException e) {
LogUtil.e(e, "Fail to get default AdId state.");
return false;
}
}
@Override
@RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_MANAGER)
public int getUserManualInteractionWithConsent() {
enforceAdServicesManagerPermission();
final int userIdentifier = getUserIdentifierFromBinderCallingUid();
LogUtil.v(
"wasUserManualInteractionWithConsentRecorded() for User Identifier %d",
userIdentifier);
try {
return mUserInstanceManager
.getOrCreateUserConsentManagerInstance(userIdentifier)
.getUserManualInteractionWithConsent();
} catch (IOException e) {
LogUtil.e(e, "Fail to get manual interaction with consent recorded.");
return 0;
}
}
@Override
@RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_MANAGER)
public boolean wasGaUxNotificationDisplayed() {
enforceAdServicesManagerPermission();
final int userIdentifier = getUserIdentifierFromBinderCallingUid();
LogUtil.v("wasGaUxNotificationDisplayed() for User Identifier %d", userIdentifier);
try {
return mUserInstanceManager
.getOrCreateUserConsentManagerInstance(userIdentifier)
.wasGaUxNotificationDisplayed();
} catch (IOException e) {
LogUtil.e(e, "Fail to get the wasGaUxNotificationDisplayed.");
return false;
}
}
/** retrieves the default consent of a user. */
@RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_MANAGER)
public boolean getDefaultConsent() {
enforceAdServicesManagerPermission();
final int userIdentifier = getUserIdentifierFromBinderCallingUid();
LogUtil.v("getDefaultConsent() for User Identifier %d", userIdentifier);
try {
return mUserInstanceManager
.getOrCreateUserConsentManagerInstance(userIdentifier)
.getDefaultConsent();
} catch (IOException e) {
LogUtil.e(e, "Fail to get the default consent: " + e.getMessage());
return false;
}
}
/** Get the currently running privacy sandbox feature on device. */
@RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_MANAGER)
public String getCurrentPrivacySandboxFeature() {
enforceAdServicesManagerPermission();
final int userIdentifier = getUserIdentifierFromBinderCallingUid();
LogUtil.v("getCurrentPrivacySandboxFeature() for User Identifier %d", userIdentifier);
try {
for (PrivacySandboxFeatureType featureType : PrivacySandboxFeatureType.values()) {
if (mUserInstanceManager
.getOrCreateUserConsentManagerInstance(userIdentifier)
.isPrivacySandboxFeatureEnabled(featureType)) {
return featureType.name();
}
}
} catch (IOException e) {
LogUtil.e(e, "Fail to get the privacy sandbox feature state: " + e.getMessage());
}
return PrivacySandboxFeatureType.PRIVACY_SANDBOX_UNSUPPORTED.name();
}
/** Set the currently running privacy sandbox feature on device. */
@Override
@RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_MANAGER)
public void setCurrentPrivacySandboxFeature(String featureType) {
enforceAdServicesManagerPermission();
final int userIdentifier = getUserIdentifierFromBinderCallingUid();
LogUtil.v("setCurrentPrivacySandboxFeature() for User Identifier %d", userIdentifier);
try {
mUserInstanceManager
.getOrCreateUserConsentManagerInstance(userIdentifier)
.setCurrentPrivacySandboxFeature(featureType);
} catch (IOException e) {
LogUtil.e(e, "Fail to set current privacy sandbox feature: " + e.getMessage());
}
}
@Override
@RequiresPermission
public List<String> getKnownAppsWithConsent(@NonNull List<String> installedPackages) {
enforceAdServicesManagerPermission();
final int userIdentifier = getUserIdentifierFromBinderCallingUid();
LogUtil.v("getKnownAppsWithConsent() for User Identifier %d", userIdentifier);
try {
return mUserInstanceManager
.getOrCreateUserAppConsentManagerInstance(userIdentifier)
.getKnownAppsWithConsent(installedPackages);
} catch (IOException e) {
LogUtil.e(
e,
"Failed to get the getKnownAppsWithConsent() for user identifier %d.",
userIdentifier);
return List.of();
}
}
@Override
@RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_MANAGER)
public List<String> getAppsWithRevokedConsent(@NonNull List<String> installedPackages) {
enforceAdServicesManagerPermission();
final int userIdentifier = getUserIdentifierFromBinderCallingUid();
LogUtil.v("getAppsWithRevokedConsent() for User Identifier %d", userIdentifier);
try {
return mUserInstanceManager
.getOrCreateUserAppConsentManagerInstance(userIdentifier)
.getAppsWithRevokedConsent(installedPackages);
} catch (IOException e) {
LogUtil.e(
e,
"Failed to getAppsWithRevokedConsent() for user identifier %d.",
userIdentifier);
return List.of();
}
}
@Override
@RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_MANAGER)
public void setConsentForApp(
@NonNull String packageName, int packageUid, boolean isConsentRevoked) {
enforceAdServicesManagerPermission();
final int userIdentifier = getUserIdentifierFromBinderCallingUid();
LogUtil.v(
"setConsentForApp() for User Identifier %d, package name %s, and package uid %d to"
+ " %s.",
userIdentifier, packageName, packageUid, isConsentRevoked);
try {
mUserInstanceManager
.getOrCreateUserAppConsentManagerInstance(userIdentifier)
.setConsentForApp(packageName, packageUid, isConsentRevoked);
} catch (IOException e) {
LogUtil.e(
e,
"Failed to setConsentForApp() for User Identifier %d, package name %s, and"
+ " package uid %d to %s.",
userIdentifier,
packageName,
packageUid,
isConsentRevoked);
}
}
@Override
@RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_MANAGER)
public void clearKnownAppsWithConsent() {
enforceAdServicesManagerPermission();
final int userIdentifier = getUserIdentifierFromBinderCallingUid();
LogUtil.v("clearKnownAppsWithConsent() for user identifier %d.", userIdentifier);
try {
mUserInstanceManager
.getOrCreateUserAppConsentManagerInstance(userIdentifier)
.clearKnownAppsWithConsent();
} catch (IOException e) {
LogUtil.e(
e,
"Failed to clearKnownAppsWithConsent() for user identifier %d",
userIdentifier);
}
}
@Override
@RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_MANAGER)
public void clearAllAppConsentData() {
enforceAdServicesManagerPermission();
final int userIdentifier = getUserIdentifierFromBinderCallingUid();
LogUtil.v("clearAllAppConsentData() for user identifier %d.", userIdentifier);
try {
mUserInstanceManager
.getOrCreateUserAppConsentManagerInstance(userIdentifier)
.clearAllAppConsentData();
} catch (IOException e) {
LogUtil.e(
e, "Failed to clearAllAppConsentData() for user identifier %d", userIdentifier);
}
}
@Override
@RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_MANAGER)
public boolean isConsentRevokedForApp(@NonNull String packageName, int packageUid)
throws IllegalArgumentException {
enforceAdServicesManagerPermission();
final int userIdentifier = getUserIdentifierFromBinderCallingUid();
LogUtil.v(
"isConsentRevokedForApp() for user identifier %d, package name %s, and package uid"
+ " %d.",
userIdentifier, packageName, packageUid);
try {
return mUserInstanceManager
.getOrCreateUserAppConsentManagerInstance(userIdentifier)
.isConsentRevokedForApp(packageName, packageUid);
} catch (IOException e) {
LogUtil.e(
e,
"Failed to call isConsentRevokedForApp() for user identifier %d, package name"
+ " %s, and package uid %d.",
userIdentifier,
packageName,
packageUid);
return true;
}
}
@Override
@RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_MANAGER)
public boolean setConsentForAppIfNew(
@NonNull String packageName, int packageUid, boolean isConsentRevoked)
throws IllegalArgumentException {
enforceAdServicesManagerPermission();
final int userIdentifier = getUserIdentifierFromBinderCallingUid();
LogUtil.v(
"setConsentForAppIfNew() for user identifier %d, package name"
+ " %s, and package uid %d to %s.",
userIdentifier, packageName, packageUid, isConsentRevoked);
try {
return mUserInstanceManager
.getOrCreateUserAppConsentManagerInstance(userIdentifier)
.setConsentForAppIfNew(packageName, packageUid, isConsentRevoked);
} catch (IOException e) {
LogUtil.e(
e,
"Failed to setConsentForAppIfNew() for user identifier %d, package name"
+ " %s, and package uid %d to %s.",
userIdentifier,
packageName,
packageUid,
isConsentRevoked);
return true;
}
}
@Override
@RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_MANAGER)
public void clearConsentForUninstalledApp(@NonNull String packageName, int packageUid) {
enforceAdServicesManagerPermission();
final int userIdentifier = getUserIdentifierFromBinderCallingUid();
LogUtil.v(
"clearConsentForUninstalledApp() for user identifier %d, package name"
+ " %s, and package uid %d.",
userIdentifier, packageName, packageUid);
try {
mUserInstanceManager
.getOrCreateUserAppConsentManagerInstance(userIdentifier)
.clearConsentForUninstalledApp(packageName, packageUid);
} catch (IOException e) {
LogUtil.e(
e,
"Failed to clearConsentForUninstalledApp() for user identifier %d, package name"
+ " %s, and package uid %d.",
userIdentifier,
packageName,
packageUid);
}
}
@Override
@RequiresPermission(android.Manifest.permission.DUMP)
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
mContext.enforceCallingPermission(android.Manifest.permission.DUMP, /* message= */ null);
synchronized (mSetPackageVersionLock) {
pw.printf("mAdServicesModuleName: %s\n", mAdServicesModuleName);
pw.printf("mAdServicesModuleVersion: %d\n", mAdServicesModuleVersion);
}
synchronized (mRegisterReceiverLock) {
pw.printf("mHandlerThread: %s\n", mHandlerThread);
}
synchronized (mRollbackCheckLock) {
pw.printf("mAdServicesPackagesRolledBackFrom: %s\n", mAdServicesPackagesRolledBackFrom);
pw.printf("mAdServicesPackagesRolledBackTo: %s\n", mAdServicesPackagesRolledBackTo);
}
mUserInstanceManager.dump(pw, args);
}
@RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_MANAGER)
public void recordAdServicesDeletionOccurred(
@AdServicesManager.DeletionApiType int deletionType) {
enforceAdServicesManagerPermission();
final int userIdentifier = getUserIdentifierFromBinderCallingUid();
try {
LogUtil.v(
"recordAdServicesDeletionOccurred() for user identifier %d, api type %d",
userIdentifier, deletionType);
mUserInstanceManager
.getOrCreateUserRollbackHandlingManagerInstance(
userIdentifier, getAdServicesApexVersion())
.recordAdServicesDataDeletion(deletionType);
} catch (IOException e) {
LogUtil.e(e, "Failed to persist the deletion status.");
}
}
public boolean needsToHandleRollbackReconciliation(
@AdServicesManager.DeletionApiType int deletionType) {
// Check if there was at least one rollback of the AdServices module.
if (getAdServicesPackagesRolledBackFrom().isEmpty()) {
return false;
}
// Check if the deletion bit is set.
if (!hasAdServicesDeletionOccurred(deletionType)) {
return false;
}
// For each rollback, check if the rolled back from version matches the previously stored
// version and the rolled back to version matches the current version.
int previousStoredVersion = getPreviousStoredVersion(deletionType);
for (Integer rollbackId : getAdServicesPackagesRolledBackFrom().keySet()) {
if (getAdServicesPackagesRolledBackFrom().get(rollbackId).getLongVersionCode()
== previousStoredVersion
&& getAdServicesPackagesRolledBackTo().get(rollbackId).getLongVersionCode()
== getAdServicesApexVersion()) {
resetAdServicesDeletionOccurred(deletionType);
return true;
}
}
// None of the stored rollbacks match the versions.
return false;
}
@VisibleForTesting
boolean hasAdServicesDeletionOccurred(@AdServicesManager.DeletionApiType int deletionType) {
enforceAdServicesManagerPermission();
final int userIdentifier = getUserIdentifierFromBinderCallingUid();
try {
LogUtil.v(
"hasAdServicesDeletionOccurred() for user identifier %d, api type %d",
userIdentifier, deletionType);
return mUserInstanceManager
.getOrCreateUserRollbackHandlingManagerInstance(
userIdentifier, getAdServicesApexVersion())
.wasAdServicesDataDeleted(deletionType);
} catch (IOException e) {
LogUtil.e(e, "Failed to retrieve the deletion status.");
return false;
}
}
@VisibleForTesting
void resetAdServicesDeletionOccurred(@AdServicesManager.DeletionApiType int deletionType) {
enforceAdServicesManagerPermission();
final int userIdentifier = getUserIdentifierFromBinderCallingUid();
try {
LogUtil.v("resetMeasurementDeletionOccurred() for user identifier %d", userIdentifier);
mUserInstanceManager
.getOrCreateUserRollbackHandlingManagerInstance(
userIdentifier, getAdServicesApexVersion())
.resetAdServicesDataDeletion(deletionType);
} catch (IOException e) {
LogUtil.e(e, "Failed to remove the measurement deletion status.");
}
}
@VisibleForTesting
int getPreviousStoredVersion(@AdServicesManager.DeletionApiType int deletionType) {
enforceAdServicesManagerPermission();
final int userIdentifier = getUserIdentifierFromBinderCallingUid();
try {
return mUserInstanceManager
.getOrCreateUserRollbackHandlingManagerInstance(
userIdentifier, getAdServicesApexVersion())
.getPreviousStoredVersion(deletionType);
} catch (IOException e) {
LogUtil.e(e, "Failed to get the previous version stored in the datastore file.");
return 0;
}
}
@VisibleForTesting
void registerReceivers() {
// There could be race condition between registerReceivers call
// in the AdServicesManagerService constructor and the mOnFlagsChangedListener.
synchronized (mRegisterReceiverLock) {
if (!FlagsFactory.getFlags().getAdServicesSystemServiceEnabled()) {
LogUtil.d("AdServicesSystemServiceEnabled is FALSE.");
// If there is a SystemServicePackageChangeReceiver, unregister it.
if (mSystemServicePackageChangedReceiver != null) {
LogUtil.d("Unregistering the existing SystemServicePackageChangeReceiver");
mContext.unregisterReceiver(mSystemServicePackageChangedReceiver);
mSystemServicePackageChangedReceiver = null;
}
// If there is a SystemServiceUserActionReceiver, unregister it.
if (mSystemServiceUserActionReceiver != null) {
LogUtil.d("Unregistering the existing SystemServiceUserActionReceiver");
mContext.unregisterReceiver(mSystemServiceUserActionReceiver);
mSystemServiceUserActionReceiver = null;
}
if (mHandler != null) {
mHandlerThread.quitSafely();
mHandler = null;
}
return;
}
// Start the handler thread.
if (mHandler == null) {
mHandlerThread = new HandlerThread("AdServicesManagerServiceHandler");
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
}
registerPackagedChangedBroadcastReceiversLocked();
registerUserActionBroadcastReceiverLocked();
}
}
@VisibleForTesting
/**
* Stores the AdServices module version locally. Users other than the main user do not have the
* permission to get the version through the PackageManager, so we have to get the version when
* the AdServices system service starts.
*/
void setAdServicesApexVersion() {
synchronized (mSetPackageVersionLock) {
if (!FlagsFactory.getFlags().getAdServicesSystemServiceEnabled()) {
LogUtil.d("AdServicesSystemServiceEnabled is FALSE.");
return;
}
PackageManager packageManager = mContext.getPackageManager();
List<PackageInfo> installedPackages =
packageManager.getInstalledPackages(
PackageManager.PackageInfoFlags.of(PackageManager.MATCH_APEX));
installedPackages.forEach(
packageInfo -> {
if (packageInfo.packageName.contains("adservices") && packageInfo.isApex) {
mAdServicesModuleName = packageInfo.packageName;
mAdServicesModuleVersion = (int) packageInfo.getLongVersionCode();
}
});
}
}
@VisibleForTesting
int getAdServicesApexVersion() {
return mAdServicesModuleVersion;
}
@VisibleForTesting
/** Checks the RollbackManager to see the rollback status of the AdServices module. */
void setRollbackStatus() {
synchronized (mRollbackCheckLock) {
if (!FlagsFactory.getFlags().getAdServicesSystemServiceEnabled()) {
LogUtil.d("AdServicesSystemServiceEnabled is FALSE.");
resetRollbackArraysRCLocked();
return;
}
RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
if (rollbackManager == null) {
LogUtil.d("Failed to get the RollbackManager service.");
resetRollbackArraysRCLocked();
return;
}
List<RollbackInfo> recentlyCommittedRollbacks =
rollbackManager.getRecentlyCommittedRollbacks();
for (RollbackInfo rollbackInfo : recentlyCommittedRollbacks) {
for (PackageRollbackInfo packageRollbackInfo : rollbackInfo.getPackages()) {
if (packageRollbackInfo.getPackageName().equals(mAdServicesModuleName)) {
mAdServicesPackagesRolledBackFrom.put(
rollbackInfo.getRollbackId(),
packageRollbackInfo.getVersionRolledBackFrom());
mAdServicesPackagesRolledBackTo.put(
rollbackInfo.getRollbackId(),
packageRollbackInfo.getVersionRolledBackTo());
LogUtil.d(
"Rollback of AdServices module occurred, "
+ "from version %d to version %d",
packageRollbackInfo.getVersionRolledBackFrom().getLongVersionCode(),
packageRollbackInfo.getVersionRolledBackTo().getLongVersionCode());
}
}
}
}
}
@GuardedBy("mRollbackCheckLock")
private void resetRollbackArraysRCLocked() {
mAdServicesPackagesRolledBackFrom.clear();
mAdServicesPackagesRolledBackTo.clear();
}
@VisibleForTesting
Map<Integer, VersionedPackage> getAdServicesPackagesRolledBackFrom() {
return mAdServicesPackagesRolledBackFrom;
}
@VisibleForTesting
Map<Integer, VersionedPackage> getAdServicesPackagesRolledBackTo() {
return mAdServicesPackagesRolledBackTo;
}
/**
* Registers a receiver for any broadcasts related to user profile removal for all users on the
* device at boot up. After receiving the broadcast, we delete consent manager instance and
* remove the user related data.
*/
private void registerUserActionBroadcastReceiverLocked() {
if (mSystemServiceUserActionReceiver != null) {
// We already register the receiver.
LogUtil.d("SystemServiceUserActionReceiver is already registered.");
return;
}
mSystemServiceUserActionReceiver =
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
mHandler.post(() -> onUserRemoved(intent));
}
};
mContext.registerReceiverForAllUsers(
mSystemServiceUserActionReceiver,
new IntentFilter(Intent.ACTION_USER_REMOVED),
/* broadcastPermission= */ null,
mHandler);
LogUtil.d("SystemServiceUserActionReceiver registered.");
}
/** Deletes the user instance and remove the user consent related data. */
@VisibleForTesting
void onUserRemoved(@NonNull Intent intent) {
Objects.requireNonNull(intent);
if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) {
UserHandle userHandle = intent.getParcelableExtra(Intent.EXTRA_USER, UserHandle.class);
if (userHandle == null) {
LogUtil.e("Extra " + Intent.EXTRA_USER + " is missing in the intent: " + intent);
return;
}
LogUtil.d("Deleting user instance with user id: " + userHandle.getIdentifier());
try {
mUserInstanceManager.deleteUserInstance(userHandle.getIdentifier());
} catch (Exception e) {
LogUtil.e(e, "Failed to delete the consent manager directory");
}
}
}
/**
* Registers a receiver for any broadcasts regarding changes to any packages for all users on
* the device at boot up. After receiving the broadcast, send an explicit broadcast to the
* AdServices module as that user.
*/
private void registerPackagedChangedBroadcastReceiversLocked() {
if (mSystemServicePackageChangedReceiver != null) {
// We already register the receiver.
LogUtil.d("SystemServicePackageChangedReceiver is already registered.");
return;
}
final IntentFilter packageChangedIntentFilter = new IntentFilter();
packageChangedIntentFilter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
packageChangedIntentFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
packageChangedIntentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
packageChangedIntentFilter.addDataScheme("package");
mSystemServicePackageChangedReceiver =
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
UserHandle user = getSendingUser();
mHandler.post(() -> onPackageChange(intent, user));
}
};
mContext.registerReceiverForAllUsers(
mSystemServicePackageChangedReceiver,
packageChangedIntentFilter,
/* broadcastPermission */ null,
mHandler);
LogUtil.d("Package changed broadcast receivers registered.");
}
/** Sends an explicit broadcast to the AdServices module when a package change occurs. */
@VisibleForTesting
public void onPackageChange(Intent intent, UserHandle user) {
Intent explicitBroadcast = new Intent();
explicitBroadcast.setAction(PACKAGE_CHANGED_BROADCAST);
explicitBroadcast.setData(intent.getData());
final Intent i = new Intent(PACKAGE_CHANGED_BROADCAST);
final List<ResolveInfo> resolveInfo =
mContext.getPackageManager()
.queryBroadcastReceiversAsUser(
i,
PackageManager.ResolveInfoFlags.of(PackageManager.GET_RECEIVERS),
user);
if (resolveInfo != null && !resolveInfo.isEmpty()) {
for (ResolveInfo info : resolveInfo) {
explicitBroadcast.setClassName(
info.activityInfo.packageName, info.activityInfo.name);
int uidChanged = intent.getIntExtra(Intent.EXTRA_UID, -1);
LogUtil.v("Package changed with UID " + uidChanged);
explicitBroadcast.putExtra(Intent.EXTRA_UID, uidChanged);
switch (intent.getAction()) {
case Intent.ACTION_PACKAGE_DATA_CLEARED:
explicitBroadcast.putExtra(ACTION_KEY, PACKAGE_DATA_CLEARED);
mContext.sendBroadcastAsUser(explicitBroadcast, user);
break;
case Intent.ACTION_PACKAGE_FULLY_REMOVED:
// TODO (b/233373604): Propagate broadcast to users not currently running
explicitBroadcast.putExtra(ACTION_KEY, PACKAGE_FULLY_REMOVED);
mContext.sendBroadcastAsUser(explicitBroadcast, user);
break;
case Intent.ACTION_PACKAGE_ADDED:
explicitBroadcast.putExtra(ACTION_KEY, PACKAGE_ADDED);
// For users where the app is merely being updated rather than added, we
// don't want to send the broadcast.
if (!intent.getExtras().getBoolean(Intent.EXTRA_REPLACING, false)) {
mContext.sendBroadcastAsUser(explicitBroadcast, user);
}
break;
}
}
}
}
// Check if caller has permission to invoke AdServicesManager APIs.
@VisibleForTesting
void enforceAdServicesManagerPermission() {
mContext.enforceCallingPermission(
AdServicesPermissions.ACCESS_ADSERVICES_MANAGER,
ERROR_MESSAGE_NOT_PERMITTED_TO_CALL_ADSERVICESMANAGER_API);
}
}