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 41 IDataAccessService mDataAccessService; 42 43 /** @hide */ LocalDataImpl(@onNull IDataAccessService binder)44 public LocalDataImpl(@NonNull IDataAccessService binder) { 45 mDataAccessService = Objects.requireNonNull(binder); 46 } 47 48 @Override @Nullable get(@onNull String key)49 public byte[] get(@NonNull String key) { 50 final long startTimeMillis = System.currentTimeMillis(); 51 Objects.requireNonNull(key); 52 Bundle params = new Bundle(); 53 params.putString(Constants.EXTRA_LOOKUP_KEYS, key); 54 return handleLookupRequest( 55 Constants.DATA_ACCESS_OP_LOCAL_DATA_LOOKUP, params, 56 Constants.API_NAME_LOCAL_DATA_GET, startTimeMillis); 57 } 58 59 @Override @Nullable put(@onNull String key, byte[] value)60 public byte[] put(@NonNull String key, byte[] value) { 61 final long startTimeMillis = System.currentTimeMillis(); 62 Objects.requireNonNull(key); 63 Bundle params = new Bundle(); 64 params.putString(Constants.EXTRA_LOOKUP_KEYS, key); 65 params.putParcelable(Constants.EXTRA_VALUE, new ByteArrayParceledSlice(value)); 66 return handleLookupRequest( 67 Constants.DATA_ACCESS_OP_LOCAL_DATA_PUT, params, 68 Constants.API_NAME_LOCAL_DATA_PUT, startTimeMillis); 69 } 70 71 @Override @Nullable remove(@onNull String key)72 public byte[] remove(@NonNull String key) { 73 final long startTimeMillis = System.currentTimeMillis(); 74 Objects.requireNonNull(key); 75 Bundle params = new Bundle(); 76 params.putString(Constants.EXTRA_LOOKUP_KEYS, key); 77 return handleLookupRequest( 78 Constants.DATA_ACCESS_OP_LOCAL_DATA_REMOVE, params, 79 Constants.API_NAME_LOCAL_DATA_REMOVE, startTimeMillis); 80 } 81 handleLookupRequest( int op, Bundle params, int apiName, long startTimeMillis)82 private byte[] handleLookupRequest( 83 int op, Bundle params, int apiName, long startTimeMillis) { 84 int responseCode = Constants.STATUS_SUCCESS; 85 try { 86 Bundle result = handleAsyncRequest(op, params); 87 ByteArrayParceledSlice data = result.getParcelable( 88 Constants.EXTRA_RESULT, ByteArrayParceledSlice.class); 89 if (null == data) { 90 return null; 91 } 92 return data.getByteArray(); 93 } catch (RuntimeException e) { 94 responseCode = Constants.STATUS_INTERNAL_ERROR; 95 throw e; 96 } finally { 97 try { 98 mDataAccessService.logApiCallStats( 99 apiName, 100 System.currentTimeMillis() - startTimeMillis, 101 responseCode); 102 } catch (Exception e) { 103 sLogger.d(e, TAG + ": failed to log metrics"); 104 } 105 } 106 } 107 108 @Override @NonNull keySet()109 public Set<String> keySet() { 110 final long startTimeMillis = System.currentTimeMillis(); 111 int responseCode = Constants.STATUS_SUCCESS; 112 try { 113 Bundle result = handleAsyncRequest(Constants.DATA_ACCESS_OP_LOCAL_DATA_KEYSET, 114 Bundle.EMPTY); 115 HashSet<String> resultSet = 116 result.getSerializable(Constants.EXTRA_RESULT, HashSet.class); 117 if (null == resultSet) { 118 return Collections.emptySet(); 119 } 120 return resultSet; 121 } catch (RuntimeException e) { 122 responseCode = Constants.STATUS_INTERNAL_ERROR; 123 throw e; 124 } finally { 125 try { 126 mDataAccessService.logApiCallStats( 127 Constants.API_NAME_LOCAL_DATA_KEYSET, 128 System.currentTimeMillis() - startTimeMillis, 129 responseCode); 130 } catch (Exception e) { 131 sLogger.d(e, TAG + ": failed to log metrics"); 132 } 133 } 134 } 135 136 @Override getTableId()137 public int getTableId() { 138 return ModelId.TABLE_ID_LOCAL_DATA; 139 } 140 handleAsyncRequest(int op, Bundle params)141 private Bundle handleAsyncRequest(int op, Bundle params) { 142 try { 143 BlockingQueue<Bundle> asyncResult = new ArrayBlockingQueue<>(1); 144 mDataAccessService.onRequest( 145 op, 146 params, 147 new IDataAccessServiceCallback.Stub() { 148 @Override 149 public void onSuccess(@NonNull Bundle result) { 150 if (result != null) { 151 asyncResult.add(result); 152 } else { 153 asyncResult.add(Bundle.EMPTY); 154 } 155 } 156 157 @Override 158 public void onError(int errorCode) { 159 asyncResult.add(Bundle.EMPTY); 160 } 161 }); 162 return asyncResult.take(); 163 } catch (InterruptedException | RemoteException e) { 164 sLogger.e(TAG + ": Failed to retrieve result from localData", e); 165 throw new IllegalStateException(e); 166 } 167 } 168 } 169