1 /* 2 * Copyright (C) 2023 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 com.android.server.adservices.rollback; 18 19 import android.annotation.NonNull; 20 import android.app.adservices.AdServicesManager; 21 import android.util.ArrayMap; 22 23 import com.android.internal.annotations.VisibleForTesting; 24 import com.android.server.adservices.LogUtil; 25 import com.android.server.adservices.common.BooleanFileDatastore; 26 27 import java.io.IOException; 28 import java.util.Map; 29 import java.util.Objects; 30 31 /** 32 * Manager to store information on handling a rollback of the AdServices module. We will have one 33 * RollbackHandlingManager instance per user. 34 * 35 * @hide 36 */ 37 public final class RollbackHandlingManager { 38 static final String STORAGE_XML_IDENTIFIER = "RollbackHandlingStorageIdentifier.xml"; 39 40 static final String MSMT_FILE_PREFIX = "Measurement"; 41 42 static final String DELETION_OCCURRED_KEY = "deletion_occurred"; 43 44 static final String VERSION_KEY = "adservices_version"; 45 46 private final String mDatastoreDir; 47 private final int mPackageVersion; 48 49 private final Map<Integer, BooleanFileDatastore> mBooleanFileDatastoreMap = new ArrayMap<>(); 50 RollbackHandlingManager(@onNull String datastoreDir, int packageVersion)51 private RollbackHandlingManager(@NonNull String datastoreDir, int packageVersion) { 52 Objects.requireNonNull(datastoreDir); 53 54 mDatastoreDir = datastoreDir; 55 mPackageVersion = packageVersion; 56 } 57 58 /** Create a RollbackHandlingManager with base directory and for userIdentifier */ 59 @NonNull createRollbackHandlingManager( @onNull String baseDir, int userIdentifier, int packageVersion)60 public static RollbackHandlingManager createRollbackHandlingManager( 61 @NonNull String baseDir, int userIdentifier, int packageVersion) throws IOException { 62 Objects.requireNonNull(baseDir, "Base dir must be provided."); 63 64 // The data store is in the directore with the following path: 65 // /data/system/adservices/{user_id}/rollback/ 66 String rollbackHandlingDataStoreDir = 67 RollbackHandlingDatastoreLocationHelper.getRollbackHandlingDataStoreDirAndCreateDir( 68 baseDir, userIdentifier); 69 70 return new RollbackHandlingManager(rollbackHandlingDataStoreDir, packageVersion); 71 } 72 73 @VisibleForTesting getOrCreateBooleanFileDatastore( @dServicesManager.DeletionApiType int deletionApiType)74 BooleanFileDatastore getOrCreateBooleanFileDatastore( 75 @AdServicesManager.DeletionApiType int deletionApiType) throws IOException { 76 synchronized (this) { 77 BooleanFileDatastore datastore = mBooleanFileDatastoreMap.get(deletionApiType); 78 if (datastore == null) { 79 if (deletionApiType == AdServicesManager.MEASUREMENT_DELETION) { 80 datastore = 81 new BooleanFileDatastore( 82 mDatastoreDir, 83 MSMT_FILE_PREFIX + STORAGE_XML_IDENTIFIER, 84 mPackageVersion, 85 VERSION_KEY); 86 } 87 mBooleanFileDatastoreMap.put(deletionApiType, datastore); 88 datastore.initialize(); 89 } 90 return datastore; 91 } 92 } 93 94 /** Saves that a deletion of AdServices data occurred to the storage. */ recordAdServicesDataDeletion(@dServicesManager.DeletionApiType int deletionType)95 public void recordAdServicesDataDeletion(@AdServicesManager.DeletionApiType int deletionType) 96 throws IOException { 97 BooleanFileDatastore datastore = getOrCreateBooleanFileDatastore(deletionType); 98 synchronized (this) { 99 try { 100 datastore.put(DELETION_OCCURRED_KEY, /* value */ true); 101 } catch (IOException e) { 102 LogUtil.e(e, "Record deletion failed due to IOException thrown by Datastore."); 103 } 104 } 105 } 106 107 /** Returns information about whether a deletion of AdServices data occurred. */ wasAdServicesDataDeleted(@dServicesManager.DeletionApiType int deletionType)108 public boolean wasAdServicesDataDeleted(@AdServicesManager.DeletionApiType int deletionType) 109 throws IOException { 110 BooleanFileDatastore datastore = getOrCreateBooleanFileDatastore(deletionType); 111 synchronized (this) { 112 return datastore.get(DELETION_OCCURRED_KEY, /* defaultValue */ false); 113 } 114 } 115 116 /** Returns the previous version number saved in the datastore file. */ getPreviousStoredVersion(@dServicesManager.DeletionApiType int deletionType)117 public int getPreviousStoredVersion(@AdServicesManager.DeletionApiType int deletionType) 118 throws IOException { 119 BooleanFileDatastore datastore = getOrCreateBooleanFileDatastore(deletionType); 120 return datastore.getPreviousStoredVersion(); 121 } 122 123 /** 124 * Deletes the previously stored information about whether a deletion of AdServices data 125 * occurred. 126 */ resetAdServicesDataDeletion(@dServicesManager.DeletionApiType int deletionType)127 public void resetAdServicesDataDeletion(@AdServicesManager.DeletionApiType int deletionType) 128 throws IOException { 129 BooleanFileDatastore datastore = getOrCreateBooleanFileDatastore(deletionType); 130 synchronized (this) { 131 try { 132 datastore.put(DELETION_OCCURRED_KEY, /* value */ false); 133 } catch (IOException e) { 134 LogUtil.e( 135 e, "Reset deletion status failed due to IOException thrown by Datastore."); 136 } 137 } 138 } 139 140 /** tesrDown method used for testing only. */ 141 @VisibleForTesting tearDownForTesting()142 public void tearDownForTesting() { 143 synchronized (this) { 144 mBooleanFileDatastoreMap.clear(); 145 } 146 } 147 } 148