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.adservices.shared.storage.BooleanFileDatastore; 24 import com.android.internal.annotations.VisibleForTesting; 25 import com.android.server.adservices.LogUtil; 26 27 import java.io.IOException; 28 import java.io.PrintWriter; 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 @VisibleForTesting static final String DUMP_PREFIX = " "; 47 48 private final String mDatastoreDir; 49 private final int mPackageVersion; 50 51 private final ArrayMap<Integer, BooleanFileDatastore> mBooleanFileDatastoreMap = 52 new ArrayMap<>(); 53 RollbackHandlingManager(@onNull String datastoreDir, int packageVersion)54 private RollbackHandlingManager(@NonNull String datastoreDir, int packageVersion) { 55 Objects.requireNonNull(datastoreDir); 56 57 mDatastoreDir = datastoreDir; 58 mPackageVersion = packageVersion; 59 } 60 61 /** Create a RollbackHandlingManager with base directory and for userIdentifier */ 62 @NonNull createRollbackHandlingManager( @onNull String baseDir, int userIdentifier, int packageVersion)63 public static RollbackHandlingManager createRollbackHandlingManager( 64 @NonNull String baseDir, int userIdentifier, int packageVersion) throws IOException { 65 Objects.requireNonNull(baseDir, "Base dir must be provided."); 66 67 // The data store is in the directore with the following path: 68 // /data/system/adservices/{user_id}/rollback/ 69 String rollbackHandlingDataStoreDir = 70 RollbackHandlingDatastoreLocationHelper.getRollbackHandlingDataStoreDirAndCreateDir( 71 baseDir, userIdentifier); 72 73 return new RollbackHandlingManager(rollbackHandlingDataStoreDir, packageVersion); 74 } 75 76 @VisibleForTesting getOrCreateBooleanFileDatastore( @dServicesManager.DeletionApiType int deletionApiType)77 BooleanFileDatastore getOrCreateBooleanFileDatastore( 78 @AdServicesManager.DeletionApiType int deletionApiType) throws IOException { 79 synchronized (this) { 80 BooleanFileDatastore datastore = mBooleanFileDatastoreMap.get(deletionApiType); 81 if (datastore == null) { 82 if (deletionApiType == AdServicesManager.MEASUREMENT_DELETION) { 83 datastore = 84 new BooleanFileDatastore( 85 mDatastoreDir, 86 MSMT_FILE_PREFIX + STORAGE_XML_IDENTIFIER, 87 mPackageVersion, 88 VERSION_KEY); 89 } 90 mBooleanFileDatastoreMap.put(deletionApiType, datastore); 91 datastore.initialize(); 92 } 93 return datastore; 94 } 95 } 96 97 /** Saves that a deletion of AdServices data occurred to the storage. */ recordAdServicesDataDeletion(@dServicesManager.DeletionApiType int deletionType)98 public void recordAdServicesDataDeletion(@AdServicesManager.DeletionApiType int deletionType) 99 throws IOException { 100 BooleanFileDatastore datastore = getOrCreateBooleanFileDatastore(deletionType); 101 synchronized (this) { 102 try { 103 datastore.put(DELETION_OCCURRED_KEY, /* value */ true); 104 } catch (IOException e) { 105 LogUtil.e(e, "Record deletion failed due to IOException thrown by Datastore."); 106 } 107 } 108 } 109 110 /** Returns information about whether a deletion of AdServices data occurred. */ wasAdServicesDataDeleted(@dServicesManager.DeletionApiType int deletionType)111 public boolean wasAdServicesDataDeleted(@AdServicesManager.DeletionApiType int deletionType) 112 throws IOException { 113 BooleanFileDatastore datastore = getOrCreateBooleanFileDatastore(deletionType); 114 synchronized (this) { 115 return datastore.get(DELETION_OCCURRED_KEY, /* defaultValue */ false); 116 } 117 } 118 119 /** Returns the previous version number saved in the datastore file. */ getPreviousStoredVersion(@dServicesManager.DeletionApiType int deletionType)120 public int getPreviousStoredVersion(@AdServicesManager.DeletionApiType int deletionType) 121 throws IOException { 122 BooleanFileDatastore datastore = getOrCreateBooleanFileDatastore(deletionType); 123 return datastore.getPreviousStoredVersion(); 124 } 125 126 /** 127 * Deletes the previously stored information about whether a deletion of AdServices data 128 * occurred. 129 */ resetAdServicesDataDeletion(@dServicesManager.DeletionApiType int deletionType)130 public void resetAdServicesDataDeletion(@AdServicesManager.DeletionApiType int deletionType) 131 throws IOException { 132 BooleanFileDatastore datastore = getOrCreateBooleanFileDatastore(deletionType); 133 synchronized (this) { 134 try { 135 datastore.put(DELETION_OCCURRED_KEY, /* value */ false); 136 } catch (IOException e) { 137 LogUtil.e( 138 e, "Reset deletion status failed due to IOException thrown by Datastore."); 139 } 140 } 141 } 142 143 /** Dumps its internal state. */ dump(PrintWriter writer, String prefix)144 public void dump(PrintWriter writer, String prefix) { 145 writer.printf("%sRollbackHandlingManager:\n", prefix); 146 String prefix2 = prefix + DUMP_PREFIX; 147 148 writer.printf("%smDatastoreDir: %s\n", prefix2, mDatastoreDir); 149 writer.printf("%smPackageVersion: %s\n", prefix2, mPackageVersion); 150 151 int mapSize = mBooleanFileDatastoreMap.size(); 152 writer.printf("%s%d datastores", prefix2, mapSize); 153 if (mapSize == 0) { 154 writer.println(); 155 return; 156 } 157 writer.println(':'); 158 String prefix3 = prefix2 + DUMP_PREFIX; 159 String prefix4 = prefix3 + DUMP_PREFIX; 160 for (int i = 0; i < mapSize; i++) { 161 writer.printf("%s deletion API type %d:\n", prefix3, mBooleanFileDatastoreMap.keyAt(i)); 162 mBooleanFileDatastoreMap.valueAt(i).dump(writer, prefix4); 163 } 164 } 165 166 /** tesrDown method used for testing only. */ 167 @VisibleForTesting tearDownForTesting()168 public void tearDownForTesting() { 169 synchronized (this) { 170 mBooleanFileDatastoreMap.clear(); 171 } 172 } 173 } 174