1 /* 2 * Copyright (C) 2022 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.uwb.pm; 18 19 import static com.android.server.uwb.data.ServiceProfileData.ServiceProfileInfo.ADF_STATUS_CREATED; 20 import static com.android.server.uwb.data.ServiceProfileData.ServiceProfileInfo.ADF_STATUS_NOT_PROVISIONED; 21 import static com.android.server.uwb.data.ServiceProfileData.ServiceProfileInfo.ADF_STATUS_PROVISIONED; 22 23 import static com.google.uwb.support.fira.FiraParams.PACS_PROFILE_SERVICE_ID; 24 25 import android.content.AttributionSource; 26 import android.content.Context; 27 import android.os.Binder; 28 import android.os.Handler; 29 import android.util.Log; 30 import android.uwb.IUwbRangingCallbacks; 31 import android.uwb.SessionHandle; 32 33 import androidx.annotation.NonNull; 34 35 import com.android.server.uwb.UwbConfigStore; 36 import com.android.server.uwb.UwbInjector; 37 import com.android.server.uwb.data.ServiceProfileData; 38 import com.android.server.uwb.data.ServiceProfileData.ServiceProfileInfo; 39 import com.android.server.uwb.data.UwbUciConstants; 40 import com.android.server.uwb.secure.SecureFactory; 41 import com.android.server.uwb.secure.provisioning.ProvisioningManager; 42 import com.android.server.uwb.util.ObjectIdentifier; 43 44 import com.google.uwb.support.fira.FiraParams.ServiceID; 45 46 import java.util.ArrayList; 47 import java.util.HashMap; 48 import java.util.List; 49 import java.util.Map; 50 import java.util.Optional; 51 import java.util.UUID; 52 53 public class ProfileManager { 54 55 private static final String LOG_TAG = "UwbServiceProfileStore"; 56 57 public final Map<UUID, ServiceProfileInfo> mServiceProfileMap = 58 new HashMap<>(); 59 60 public final Map<Integer, List<ServiceProfileInfo>> mAppServiceProfileMap = 61 new HashMap<>(); 62 63 public final Map<SessionHandle, RangingSessionController> mRangingSessionTable = 64 new HashMap<>(); 65 66 private static final int MAX_RETRIES = 10; 67 68 private final Context mContext; 69 private final Handler mHandler; 70 private final UwbConfigStore mUwbConfigStore; 71 private final UwbInjector mUwbInjector; 72 73 private boolean mHasNewDataToSerialize = false; 74 75 ProfileManager(@onNull Context context, @NonNull Handler handler, @NonNull UwbConfigStore uwbConfigStore, UwbInjector uwbInjector)76 public ProfileManager(@NonNull Context context, @NonNull Handler handler, @NonNull 77 UwbConfigStore uwbConfigStore, UwbInjector uwbInjector) { 78 mContext = context; 79 mHandler = handler; 80 mUwbConfigStore = uwbConfigStore; 81 mUwbInjector = uwbInjector; 82 mUwbConfigStore.registerStoreData(mUwbInjector 83 .makeServiceProfileData(new ServiceProfileStoreData())); 84 } 85 86 private class ServiceProfileStoreData implements 87 ServiceProfileData.DataSource { 88 89 @Override toSerialize()90 public Map<UUID, ServiceProfileInfo> toSerialize() { 91 mHasNewDataToSerialize = false; 92 return mServiceProfileMap; 93 } 94 95 @Override fromDeserialized(Map<UUID, ServiceProfileInfo> serviceProfileDataMap)96 public void fromDeserialized(Map<UUID, ServiceProfileInfo> serviceProfileDataMap) { 97 loadServiceProfile(serviceProfileDataMap); 98 } 99 100 @Override reset()101 public void reset() { 102 mServiceProfileMap.clear(); 103 mAppServiceProfileMap.clear(); 104 } 105 106 @Override hasNewDataToSerialize()107 public boolean hasNewDataToSerialize() { 108 return mHasNewDataToSerialize; 109 } 110 } 111 112 /** Check whether profile manager has an instance of SessionHandle */ hasSession(SessionHandle sessionHandle)113 public boolean hasSession(SessionHandle sessionHandle) { 114 return mRangingSessionTable.containsKey(sessionHandle); 115 } 116 addServiceProfile(@erviceID int serviceID)117 public Optional<UUID> addServiceProfile(@ServiceID int serviceID) { 118 UUID serviceInstanceID; 119 int app_uid = Binder.getCallingUid(); 120 int num_tries = 0; 121 do { 122 serviceInstanceID = UUID.randomUUID(); 123 num_tries++; 124 } while(mServiceProfileMap.containsKey(serviceInstanceID) && num_tries < MAX_RETRIES); 125 126 if (num_tries == MAX_RETRIES) { 127 return Optional.empty(); 128 } 129 ServiceProfileInfo serviceProfileInfo = new ServiceProfileInfo(serviceInstanceID, 130 Binder.getCallingUid(), mContext.getPackageName(), serviceID); 131 mServiceProfileMap.put(serviceInstanceID, serviceProfileInfo); 132 if (mAppServiceProfileMap.containsKey(app_uid)) { 133 List<ServiceProfileInfo> appServiceProfileList = mAppServiceProfileMap.get(app_uid); 134 appServiceProfileList.add(serviceProfileInfo); 135 mAppServiceProfileMap.put(app_uid, appServiceProfileList); 136 } else { 137 List<ServiceProfileInfo> appServiceProfileList = new ArrayList(); 138 appServiceProfileList.add(serviceProfileInfo); 139 mAppServiceProfileMap.put(app_uid, appServiceProfileList); 140 } 141 mHasNewDataToSerialize = true; 142 mHandler.post(() -> mUwbConfigStore.saveToStore(true)); 143 return Optional.of(serviceInstanceID); 144 } 145 146 /** Remove existing service profile from profile manager */ removeServiceProfile(UUID serviceInstanceID)147 public int removeServiceProfile(UUID serviceInstanceID) { 148 int app_uid = Binder.getCallingUid(); 149 if (mServiceProfileMap.containsKey(serviceInstanceID)) { 150 ServiceProfileInfo serviceProfileInfo = mServiceProfileMap.get(serviceInstanceID); 151 mServiceProfileMap.remove(serviceInstanceID); 152 153 if (mAppServiceProfileMap.containsKey(app_uid)) { 154 List<ServiceProfileInfo> appServiceProfileList = 155 mAppServiceProfileMap.get(app_uid); 156 appServiceProfileList.remove(serviceProfileInfo); 157 if (appServiceProfileList.isEmpty()) { 158 mAppServiceProfileMap.remove(app_uid); 159 } else { 160 mAppServiceProfileMap.put(app_uid, appServiceProfileList); 161 } 162 } 163 } 164 else { 165 return UwbUciConstants.STATUS_CODE_FAILED; 166 } 167 mHandler.post(() -> mUwbConfigStore.saveToStore(true)); 168 return UwbUciConstants.STATUS_CODE_OK; 169 } 170 171 /** 172 * Create ADF, provision created ADF, import ADF or delete ADF with the CMS signed, 173 * encrypted script. 174 */ 175 provisioningAdf(@onNull UUID serviceInstanceId, @NonNull byte[] script, AdfOpCallback adfOpCallback)176 public void provisioningAdf(@NonNull UUID serviceInstanceId, @NonNull byte[] script, 177 AdfOpCallback adfOpCallback) { 178 ServiceProfileInfo serviceProfileInfo = mServiceProfileMap.get(serviceInstanceId); 179 if (serviceProfileInfo == null) { 180 Log.e(LOG_TAG, "service profile info is not available, is it initialized?"); 181 adfOpCallback.onFailure(serviceInstanceId); 182 return; 183 } 184 185 ProvisioningManager provisioningManager = 186 SecureFactory.makeProvisioningManager(mContext, mHandler.getLooper()); 187 188 provisioningManager.provisioningAdf(serviceInstanceId, script, 189 new ProvisioningManager.ProvisioningCallback() { 190 @Override 191 public void onAdfCreated(@NonNull UUID serviceInstanceId, 192 @androidx.annotation.NonNull ObjectIdentifier adfOid) { 193 tryToCleanUpStaleAdfOnNewAdfAvailable( 194 serviceInstanceId, serviceProfileInfo, adfOid); 195 serviceProfileInfo.setServiceAdfOid(adfOid); 196 serviceProfileInfo.setAdfStatus(ADF_STATUS_CREATED); 197 mHandler.post(() -> mUwbConfigStore.saveToStore(/* forceWrite= */ true)); 198 adfOpCallback.onSuccess(serviceInstanceId, adfOid, AdfOp.CREATE_ADF); 199 } 200 201 @Override 202 public void onAdfProvisioned( 203 @NonNull UUID serviceInstanceId, 204 @NonNull ObjectIdentifier adfOid) { 205 if (serviceProfileInfo.getServiceAdfOid().isEmpty() 206 || !adfOid.equals(serviceProfileInfo.getServiceAdfOid().get())) { 207 Log.e(LOG_TAG, 208 "something wrong, the ADF wasn't created before provisioning"); 209 serviceProfileInfo.setServiceAdfOid(adfOid); 210 } 211 serviceProfileInfo.setAdfStatus(ADF_STATUS_PROVISIONED); 212 mHandler.post(() -> mUwbConfigStore.saveToStore(/* forceWrite= */ true)); 213 adfOpCallback.onSuccess(serviceInstanceId, adfOid, AdfOp.PROVISIONING_ADF); 214 } 215 216 @Override 217 public void onAdfImported(@NonNull UUID serviceInstanceId, 218 @NonNull ObjectIdentifier adfOid, 219 @NonNull byte[] secureBlob) { 220 tryToCleanUpStaleAdfOnNewAdfAvailable( 221 serviceInstanceId, serviceProfileInfo, adfOid); 222 serviceProfileInfo.setServiceAdfOid(adfOid); 223 serviceProfileInfo.setSecureBlob(secureBlob); 224 serviceProfileInfo.setAdfStatus(ADF_STATUS_PROVISIONED); 225 mHandler.post(() -> mUwbConfigStore.saveToStore(/* forceWrite= */ true)); 226 adfOpCallback.onSuccess(serviceInstanceId, adfOid, AdfOp.IMPORT_ADF); 227 } 228 229 @Override 230 public void onAdfDeleted(@NonNull UUID serviceInstanceId, 231 @NonNull ObjectIdentifier adfOid) { 232 serviceProfileInfo.setServiceAdfOid(null); 233 serviceProfileInfo.setAdfStatus(ADF_STATUS_NOT_PROVISIONED); 234 mHandler.post(() -> mUwbConfigStore.saveToStore(/* forceWrite= */ true)); 235 adfOpCallback.onSuccess(serviceInstanceId, adfOid, AdfOp.DELETE_ADF); 236 } 237 238 @Override 239 public void onFail(@androidx.annotation.NonNull UUID serviceInstanceId) { 240 adfOpCallback.onFailure(serviceInstanceId); 241 } 242 }); 243 } 244 tryToCleanUpStaleAdfOnNewAdfAvailable(UUID serviceInstanceId, ServiceProfileInfo serviceProfileInfo, ObjectIdentifier newAdfOid)245 private void tryToCleanUpStaleAdfOnNewAdfAvailable(UUID serviceInstanceId, 246 ServiceProfileInfo serviceProfileInfo, 247 ObjectIdentifier newAdfOid) { 248 if (serviceProfileInfo.getServiceAdfOid().isEmpty() 249 || newAdfOid.equals(serviceProfileInfo.getServiceAdfOid().get())) { 250 return; 251 } 252 Log.w(LOG_TAG, "The old ADF should be deleted, only 1 ADF is allowed."); 253 if (serviceProfileInfo.getSecureBlob().isPresent()) { 254 serviceProfileInfo.setServiceAdfOid(null); 255 serviceProfileInfo.setSecureBlob(null); 256 return; 257 } 258 259 ProvisioningManager provisioningManager = 260 SecureFactory.makeProvisioningManager(mContext, mHandler.getLooper()); 261 // overwrite anyway 262 serviceProfileInfo.setServiceAdfOid(null); 263 provisioningManager.deleteAdf(serviceInstanceId, 264 serviceProfileInfo.getServiceAdfOid().get(), 265 new ProvisioningManager.DeleteAdfCallback() { 266 @Override 267 public void onSuccess(UUID serviceInstanceId, ObjectIdentifier adfOid) { 268 Log.d(LOG_TAG, "old ADF is deleted."); 269 } 270 271 @Override 272 public void onFail(UUID serviceInstanceId, ObjectIdentifier adfOid) { 273 // TODO: add the adfOid in a garbage queue, remove later. 274 Log.e(LOG_TAG, "old ADF is not deleted."); 275 } 276 }); 277 } 278 279 /** Deletes the ADF associated with the service instance. */ deleteAdf(UUID serviceInstanceId, AdfOpCallback adfOpCallback)280 public void deleteAdf(UUID serviceInstanceId, AdfOpCallback adfOpCallback) { 281 ServiceProfileInfo serviceProfileInfo = mServiceProfileMap.get(serviceInstanceId); 282 if (serviceProfileInfo == null || serviceProfileInfo.getServiceAdfOid().isEmpty()) { 283 adfOpCallback.onFailure(serviceInstanceId); 284 return; 285 } 286 if (serviceProfileInfo.getSecureBlob().isPresent()) { 287 serviceProfileInfo.setServiceAdfOid(null); 288 serviceProfileInfo.setSecureBlob(null); 289 serviceProfileInfo.setAdfStatus(ADF_STATUS_NOT_PROVISIONED); 290 mHandler.post(() -> mUwbConfigStore.saveToStore(/* forceWrite= */ true)); 291 adfOpCallback.onSuccess(serviceInstanceId, 292 serviceProfileInfo.getServiceAdfOid().get(), AdfOp.DELETE_ADF); 293 } else { 294 ProvisioningManager provisioningManager = 295 SecureFactory.makeProvisioningManager(mContext, mHandler.getLooper()); 296 provisioningManager.deleteAdf(serviceInstanceId, 297 serviceProfileInfo.getServiceAdfOid().get(), 298 new ProvisioningManager.DeleteAdfCallback() { 299 @Override 300 public void onSuccess(UUID serviceInstanceId, ObjectIdentifier adfOid) { 301 serviceProfileInfo.setServiceAdfOid(null); 302 serviceProfileInfo.setAdfStatus(ADF_STATUS_NOT_PROVISIONED); 303 mHandler.post( 304 () -> mUwbConfigStore.saveToStore(/* forceWrite= */ true)); 305 adfOpCallback.onSuccess(serviceInstanceId, adfOid, AdfOp.DELETE_ADF); 306 } 307 308 @Override 309 public void onFail(UUID serviceInstanceId, ObjectIdentifier adfOid) { 310 adfOpCallback.onFailure(serviceInstanceId); 311 } 312 }); 313 } 314 315 } 316 loadServiceProfile(Map<UUID, ServiceProfileInfo> serviceProfileDataMap)317 public void loadServiceProfile(Map<UUID, ServiceProfileInfo> serviceProfileDataMap) { 318 mServiceProfileMap.clear(); 319 mAppServiceProfileMap.clear(); 320 for (Map.Entry<UUID, ServiceProfileInfo> entry : serviceProfileDataMap.entrySet()) { 321 mServiceProfileMap.put(entry.getKey(), entry.getValue()); 322 int app_uid = entry.getValue().uid; 323 if (mAppServiceProfileMap.containsKey(app_uid)) { 324 List<ServiceProfileInfo> appServiceProfileList = mAppServiceProfileMap.get(app_uid); 325 appServiceProfileList.add(entry.getValue()); 326 mAppServiceProfileMap.put(app_uid, appServiceProfileList); 327 } else { 328 List<ServiceProfileInfo> appServiceProfileList = new ArrayList(); 329 appServiceProfileList.add(entry.getValue()); 330 mAppServiceProfileMap.put(app_uid, appServiceProfileList); 331 } 332 } 333 } 334 335 //TODO Send all info related to app as a Persistable bundle getServiceProfiles()336 public List<ServiceProfileInfo> getServiceProfiles() { 337 int app_uid = Binder.getCallingUid(); 338 if (mAppServiceProfileMap.containsKey(app_uid)) { 339 return mAppServiceProfileMap.get(app_uid); 340 } 341 return null; 342 } 343 344 /** Initializes state machine and session related info */ activateProfile(AttributionSource attributionSource, SessionHandle sessionHandle, UUID serviceInstanceId, IUwbRangingCallbacks rangingCallbacks, String chipId)345 public void activateProfile(AttributionSource attributionSource, SessionHandle sessionHandle, 346 UUID serviceInstanceId, IUwbRangingCallbacks rangingCallbacks, String chipId) { 347 348 if (!mServiceProfileMap.containsKey(serviceInstanceId)) { 349 Log.e(LOG_TAG, "UUID not found"); 350 return; 351 } 352 ServiceProfileInfo profileInfo = mServiceProfileMap.get(serviceInstanceId); 353 354 switch (profileInfo.serviceID) { 355 /* Only PACS controlee/responder is supported now*/ 356 case PACS_PROFILE_SERVICE_ID : 357 RangingSessionController rangingSessionController = new PacsControleeSession( 358 sessionHandle, attributionSource, mContext, mUwbInjector, profileInfo, 359 rangingCallbacks, mHandler, chipId); 360 mRangingSessionTable.put(sessionHandle, rangingSessionController); 361 break; 362 default: 363 Log.e(LOG_TAG, "Service ID not supported yet"); 364 return; 365 } 366 367 /* Session has been initialized, notify app */ 368 try { 369 rangingCallbacks.onRangingOpened(sessionHandle); 370 Log.i(LOG_TAG, "IUwbRangingCallbacks - onRangingOpened"); 371 } catch (Exception e) { 372 Log.e(LOG_TAG, "IUwbRangingCallbacks - onRangingOpened : Failed"); 373 e.printStackTrace(); 374 } 375 } 376 377 /** Start Ranging */ startRanging(SessionHandle sessionHandle)378 public void startRanging(SessionHandle sessionHandle) { 379 if (mRangingSessionTable.containsKey(sessionHandle)) { 380 RangingSessionController rangingSessionController = mRangingSessionTable.get( 381 sessionHandle); 382 rangingSessionController.startSession(); 383 } else { 384 Log.e(LOG_TAG, "Session Handle not found"); 385 } 386 } 387 388 /** Stop Ranging, can be started again, session will not be reset */ stopRanging(SessionHandle sessionHandle)389 public void stopRanging(SessionHandle sessionHandle) { 390 if (mRangingSessionTable.containsKey(sessionHandle)) { 391 RangingSessionController rangingSessionController = mRangingSessionTable.get( 392 sessionHandle); 393 rangingSessionController.stopSession(); 394 } else { 395 Log.e(LOG_TAG, "Session Handle not found"); 396 } 397 } 398 399 /** End Ranging session, session info will be reset */ closeRanging(SessionHandle sessionHandle)400 public void closeRanging(SessionHandle sessionHandle) { 401 if (mRangingSessionTable.containsKey(sessionHandle)) { 402 RangingSessionController rangingSessionController = mRangingSessionTable.get( 403 sessionHandle); 404 rangingSessionController.closeSession(); 405 mRangingSessionTable.remove(sessionHandle); 406 } else { 407 Log.e(LOG_TAG, "Session Handle not found"); 408 } 409 } 410 411 /** Operating to the ADF. */ 412 public enum AdfOp { 413 CREATE_ADF, 414 PROVISIONING_ADF, 415 IMPORT_ADF, 416 DELETE_ADF 417 } 418 419 /** Callback for the ADF operating result.*/ 420 public interface AdfOpCallback { 421 422 /** The operating on the specified ADF is success */ onSuccess(UUID serviceInstanceId, ObjectIdentifier adfOid, AdfOp adfOp)423 void onSuccess(UUID serviceInstanceId, ObjectIdentifier adfOid, AdfOp adfOp); 424 425 /** The operating on the specified ADF is failed. */ onFailure(UUID serviceInstanceId)426 void onFailure(UUID serviceInstanceId); 427 } 428 } 429