• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 android.adservices.ondevicepersonalization;
18 
19 import android.adservices.ondevicepersonalization.aidl.IDataAccessService;
20 import android.adservices.ondevicepersonalization.aidl.IDataAccessServiceCallback;
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.os.Bundle;
24 import android.os.RemoteException;
25 
26 import com.android.ondevicepersonalization.internal.util.ByteArrayParceledSlice;
27 import com.android.ondevicepersonalization.internal.util.LoggerFactory;
28 
29 import java.util.Collections;
30 import java.util.HashSet;
31 import java.util.Objects;
32 import java.util.Set;
33 import java.util.concurrent.ArrayBlockingQueue;
34 import java.util.concurrent.BlockingQueue;
35 
36 /** @hide */
37 public class LocalDataImpl implements MutableKeyValueStore {
38     private static final String TAG = "LocalDataImpl";
39     private static final LoggerFactory.Logger sLogger = LoggerFactory.getLogger();
40     @NonNull private final IDataAccessService mDataAccessService;
41 
42     /** @hide */
LocalDataImpl(@onNull IDataAccessService binder)43     public LocalDataImpl(@NonNull IDataAccessService binder) {
44         mDataAccessService = Objects.requireNonNull(binder);
45     }
46 
47     @Override @Nullable
get(@onNull String key)48     public byte[] get(@NonNull String key) {
49         final long startTimeMillis = System.currentTimeMillis();
50         Objects.requireNonNull(key);
51         Bundle params = new Bundle();
52         params.putString(Constants.EXTRA_LOOKUP_KEYS, key);
53         return handleLookupRequest(
54                 Constants.DATA_ACCESS_OP_LOCAL_DATA_LOOKUP, params,
55                 Constants.API_NAME_LOCAL_DATA_GET, startTimeMillis);
56     }
57 
58     @Override @Nullable
put(@onNull String key, byte[] value)59     public byte[] put(@NonNull String key, byte[] value) {
60         final long startTimeMillis = System.currentTimeMillis();
61         Objects.requireNonNull(key);
62         Bundle params = new Bundle();
63         params.putString(Constants.EXTRA_LOOKUP_KEYS, key);
64         params.putParcelable(Constants.EXTRA_VALUE, new ByteArrayParceledSlice(value));
65         return handleLookupRequest(
66                 Constants.DATA_ACCESS_OP_LOCAL_DATA_PUT, params,
67                 Constants.API_NAME_LOCAL_DATA_PUT, startTimeMillis);
68     }
69 
70     @Override @Nullable
remove(@onNull String key)71     public byte[] remove(@NonNull String key) {
72         final long startTimeMillis = System.currentTimeMillis();
73         Objects.requireNonNull(key);
74         Bundle params = new Bundle();
75         params.putString(Constants.EXTRA_LOOKUP_KEYS, key);
76         return handleLookupRequest(
77                 Constants.DATA_ACCESS_OP_LOCAL_DATA_REMOVE, params,
78                 Constants.API_NAME_LOCAL_DATA_REMOVE, startTimeMillis);
79     }
80 
handleLookupRequest( int op, Bundle params, int apiName, long startTimeMillis)81     private byte[] handleLookupRequest(
82             int op, Bundle params, int apiName, long startTimeMillis) {
83         int responseCode = Constants.STATUS_SUCCESS;
84         try {
85             CallbackResult callbackResult = handleAsyncRequest(op, params);
86             if (callbackResult.mErrorCode != 0) {
87                 responseCode = callbackResult.mErrorCode;
88                 return null;
89             }
90             Bundle result = callbackResult.mResult;
91             if (result == null
92                     || result.getParcelable(Constants.EXTRA_RESULT, ByteArrayParceledSlice.class)
93                     == null) {
94                 responseCode = Constants.STATUS_SUCCESS_EMPTY_RESULT;
95                 return null;
96             }
97             ByteArrayParceledSlice data =
98                     result.getParcelable(Constants.EXTRA_RESULT, ByteArrayParceledSlice.class);
99             return data.getByteArray();
100         } catch (RuntimeException e) {
101             responseCode = Constants.STATUS_INTERNAL_ERROR;
102             throw e;
103         } finally {
104             try {
105                 mDataAccessService.logApiCallStats(
106                         apiName,
107                         System.currentTimeMillis() - startTimeMillis,
108                         responseCode);
109             } catch (Exception e) {
110                 sLogger.d(e, TAG + ": failed to log metrics");
111             }
112         }
113     }
114 
115     @Override @NonNull
keySet()116     public Set<String> keySet() {
117         final long startTimeMillis = System.currentTimeMillis();
118         int responseCode = Constants.STATUS_SUCCESS;
119         try {
120             CallbackResult callbackResult =
121                     handleAsyncRequest(Constants.DATA_ACCESS_OP_LOCAL_DATA_KEYSET, Bundle.EMPTY);
122             if (callbackResult.mErrorCode != 0) {
123                 responseCode = callbackResult.mErrorCode;
124                 return Collections.emptySet();
125             }
126             Bundle result = callbackResult.mResult;
127             if (result == null
128                     || result.getSerializable(Constants.EXTRA_RESULT, HashSet.class) == null) {
129                 responseCode = Constants.STATUS_SUCCESS_EMPTY_RESULT;
130                 return Collections.emptySet();
131             }
132             return result.getSerializable(Constants.EXTRA_RESULT, HashSet.class);
133         } catch (RuntimeException e) {
134             responseCode = Constants.STATUS_INTERNAL_ERROR;
135             throw e;
136         } finally {
137             try {
138                 mDataAccessService.logApiCallStats(
139                         Constants.API_NAME_LOCAL_DATA_KEYSET,
140                         System.currentTimeMillis() - startTimeMillis,
141                         responseCode);
142             } catch (Exception e) {
143                 sLogger.d(e, TAG + ": failed to log metrics");
144             }
145         }
146     }
147 
148     @Override
getTableId()149     public int getTableId() {
150         return ModelId.TABLE_ID_LOCAL_DATA;
151     }
152 
handleAsyncRequest(int op, Bundle params)153     private CallbackResult handleAsyncRequest(int op, Bundle params) {
154         // Blocks on the calling thread and waits for the response from the data access service.
155         try {
156             BlockingQueue<CallbackResult> asyncResult = new ArrayBlockingQueue<>(1);
157             mDataAccessService.onRequest(
158                     op,
159                     params,
160                     new IDataAccessServiceCallback.Stub() {
161                         @Override
162                         public void onSuccess(@NonNull Bundle result) {
163                             asyncResult.add(new CallbackResult(result, /* errorCode= */ 0));
164                         }
165 
166                         @Override
167                         public void onError(int errorCode) {
168                             asyncResult.add(new CallbackResult(/* result= */ null, errorCode));
169                         }
170                     });
171             return asyncResult.take();
172         } catch (InterruptedException | RemoteException e) {
173             sLogger.e(TAG + ": Failed to retrieve result from localData", e);
174             throw new IllegalStateException(e);
175         }
176     }
177 
178     private static class CallbackResult {
179         private final Bundle mResult;
180         private final int mErrorCode;
181 
CallbackResult(Bundle result, int errorCode)182         private CallbackResult(Bundle result, int errorCode) {
183             mResult = result;
184             mErrorCode = errorCode;
185         }
186     }
187 }
188