• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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