1 /* 2 * Copyright 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 android.adservices.ondevicepersonalization; 18 19 import static org.junit.Assert.assertArrayEquals; 20 import static org.junit.Assert.assertEquals; 21 import static org.junit.Assert.assertNull; 22 23 import android.adservices.ondevicepersonalization.aidl.IDataAccessService; 24 import android.adservices.ondevicepersonalization.aidl.IDataAccessServiceCallback; 25 import android.os.Bundle; 26 import android.os.RemoteException; 27 28 import androidx.test.ext.junit.runners.AndroidJUnit4; 29 import androidx.test.filters.SmallTest; 30 31 import com.android.ondevicepersonalization.internal.util.ByteArrayParceledSlice; 32 33 import org.junit.Before; 34 import org.junit.Test; 35 import org.junit.runner.RunWith; 36 37 import java.util.HashMap; 38 import java.util.HashSet; 39 import java.util.Objects; 40 import java.util.Set; 41 42 /** 43 * Unit Tests of LocalDataImpl API. 44 */ 45 @SmallTest 46 @RunWith(AndroidJUnit4.class) 47 public class LocalDataTest { 48 MutableKeyValueStore mLocalData; 49 50 @Before setup()51 public void setup() { 52 mLocalData = new LocalDataImpl( 53 IDataAccessService.Stub.asInterface( 54 new LocalDataService())); 55 } 56 57 @Test testLookupSuccess()58 public void testLookupSuccess() throws Exception { 59 assertArrayEquals(new byte[] {1, 2, 3}, mLocalData.get("a")); 60 assertArrayEquals(new byte[] {7, 8, 9}, mLocalData.get("c")); 61 assertNull(mLocalData.get("e")); 62 } 63 64 @Test testLookupError()65 public void testLookupError() { 66 // Triggers an expected error in the mock service. 67 assertNull(mLocalData.get("z")); 68 } 69 70 @Test testKeysetSuccess()71 public void testKeysetSuccess() { 72 Set<String> expectedResult = new HashSet<>(); 73 expectedResult.add("a"); 74 expectedResult.add("b"); 75 expectedResult.add("c"); 76 77 assertEquals(expectedResult, mLocalData.keySet()); 78 } 79 80 @Test testPutSuccess()81 public void testPutSuccess() throws Exception { 82 assertArrayEquals(new byte[] {1, 2, 3}, mLocalData.put("a", new byte[10])); 83 assertNull(mLocalData.put("e", new byte[] {1, 2, 3})); 84 assertArrayEquals(new byte[] {1, 2, 3}, mLocalData.get("e")); 85 } 86 87 @Test testPutError()88 public void testPutError() { 89 // Triggers an expected error in the mock service. 90 assertNull(mLocalData.put("z", new byte[10])); 91 } 92 93 @Test testRemoveSuccess()94 public void testRemoveSuccess() throws Exception { 95 assertArrayEquals(new byte[] {1, 2, 3}, mLocalData.remove("a")); 96 assertNull(mLocalData.remove("e")); 97 assertNull(mLocalData.get("a")); 98 } 99 100 @Test testRemoveError()101 public void testRemoveError() { 102 // Triggers an expected error in the mock service. 103 assertNull(mLocalData.remove("z")); 104 } 105 106 public static class LocalDataService extends IDataAccessService.Stub { 107 HashMap<String, byte[]> mContents = new HashMap<String, byte[]>(); 108 LocalDataService()109 public LocalDataService() { 110 mContents.put("a", new byte[] {1, 2, 3}); 111 mContents.put("b", new byte[] {4, 5, 6}); 112 mContents.put("c", new byte[] {7, 8, 9}); 113 } 114 115 @Override onRequest( int operation, Bundle params, IDataAccessServiceCallback callback)116 public void onRequest( 117 int operation, 118 Bundle params, 119 IDataAccessServiceCallback callback) { 120 121 if (operation == Constants.DATA_ACCESS_OP_LOCAL_DATA_KEYSET) { 122 Bundle result = new Bundle(); 123 result.putSerializable(Constants.EXTRA_RESULT, 124 new HashSet<>(mContents.keySet())); 125 try { 126 callback.onSuccess(result); 127 } catch (RemoteException e) { 128 // Ignored. 129 } 130 return; 131 } 132 133 String key = params.getString(Constants.EXTRA_LOOKUP_KEYS); 134 Objects.requireNonNull(key); 135 if (key.equals("z")) { 136 // Raise expected error. 137 try { 138 callback.onError(Constants.STATUS_INTERNAL_ERROR); 139 } catch (RemoteException e) { 140 // Ignored. 141 } 142 return; 143 } 144 ByteArrayParceledSlice parceledByteArray = params.getParcelable( 145 Constants.EXTRA_VALUE, ByteArrayParceledSlice.class); 146 byte[] value = null; 147 if (parceledByteArray != null) { 148 value = parceledByteArray.getByteArray(); 149 } 150 151 if (operation == Constants.DATA_ACCESS_OP_LOCAL_DATA_LOOKUP 152 || operation == Constants.DATA_ACCESS_OP_LOCAL_DATA_PUT 153 || operation == Constants.DATA_ACCESS_OP_LOCAL_DATA_REMOVE) { 154 byte[] existingValue = null; 155 if (mContents.containsKey(key)) { 156 existingValue = mContents.get(key); 157 } 158 if (operation == Constants.DATA_ACCESS_OP_LOCAL_DATA_REMOVE) { 159 mContents.remove(key); 160 } 161 if (operation == Constants.DATA_ACCESS_OP_LOCAL_DATA_PUT) { 162 mContents.put(key, value); 163 } 164 Bundle result = new Bundle(); 165 result.putParcelable( 166 Constants.EXTRA_RESULT, new ByteArrayParceledSlice(existingValue)); 167 try { 168 callback.onSuccess(result); 169 } catch (RemoteException e) { 170 // Ignored. 171 } 172 } 173 } 174 175 @Override logApiCallStats(int apiName, long latencyMillis, int responseCode)176 public void logApiCallStats(int apiName, long latencyMillis, int responseCode) {} 177 } 178 } 179