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