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.secure; 18 19 import static com.android.server.uwb.secure.csml.DispatchResponse.NOTIFICATION_EVENT_ID_CONTROLEE_INFO_AVAILABLE; 20 import static com.android.server.uwb.secure.csml.DispatchResponse.NOTIFICATION_EVENT_ID_RDS_AVAILABLE; 21 22 import android.os.Looper; 23 import android.util.Log; 24 25 import androidx.annotation.NonNull; 26 27 import com.android.server.uwb.pm.RunningProfileSessionInfo; 28 import com.android.server.uwb.secure.csml.ControleeInfo; 29 import com.android.server.uwb.secure.csml.CsmlUtil; 30 import com.android.server.uwb.secure.csml.DispatchResponse; 31 import com.android.server.uwb.secure.csml.GetDoCommand; 32 import com.android.server.uwb.secure.csml.PutDoCommand; 33 import com.android.server.uwb.secure.iso7816.TlvDatum; 34 import com.android.server.uwb.secure.iso7816.TlvParser; 35 import com.android.server.uwb.util.DataTypeConversionUtil; 36 37 import java.util.Optional; 38 39 /** 40 * The responder of dynamic STS session managed by the UWB controller. 41 */ 42 public class ControllerResponderSession extends ResponderSession { 43 private static final String LOG_TAG = "ControllerResponder"; 44 ControllerResponderSession( @onNull Looper workLooper, @NonNull FiRaSecureChannel fiRaSecureChannel, @NonNull Callback sessionCallback, @NonNull RunningProfileSessionInfo runningProfileSessionInfo)45 public ControllerResponderSession( 46 @NonNull Looper workLooper, 47 @NonNull FiRaSecureChannel fiRaSecureChannel, 48 @NonNull Callback sessionCallback, 49 @NonNull RunningProfileSessionInfo runningProfileSessionInfo) { 50 super(workLooper, fiRaSecureChannel, sessionCallback, runningProfileSessionInfo); 51 mIsController = true; 52 } 53 54 @Override onDispatchResponseReceived(@onNull DispatchResponse dispatchResponse)55 protected boolean onDispatchResponseReceived(@NonNull DispatchResponse dispatchResponse) { 56 DispatchResponse.RdsAvailableNotification rdsAvailable = null; 57 DispatchResponse.ControleeInfoAvailableNotification controleeInfoAvailable = null; 58 for (DispatchResponse.Notification notification : dispatchResponse.notifications) { 59 switch (notification.notificationEventId) { 60 case NOTIFICATION_EVENT_ID_RDS_AVAILABLE: 61 // Responder notification 62 rdsAvailable = (DispatchResponse.RdsAvailableNotification) notification; 63 break; 64 case NOTIFICATION_EVENT_ID_CONTROLEE_INFO_AVAILABLE: 65 controleeInfoAvailable = 66 (DispatchResponse.ControleeInfoAvailableNotification) notification; 67 break; 68 default: 69 logw( 70 "Unexpected notification from dispatch response: " 71 + notification.notificationEventId); 72 } 73 } 74 if (controleeInfoAvailable != null) { 75 handleControleeInfoAvailable(controleeInfoAvailable); 76 return true; 77 } 78 if (rdsAvailable != null) { 79 handleRdsAvailable(rdsAvailable); 80 return true; 81 } 82 return false; 83 } 84 handleControleeInfoAvailable( @onNull DispatchResponse.ControleeInfoAvailableNotification controleeInfoAvailable)85 private void handleControleeInfoAvailable( 86 @NonNull DispatchResponse.ControleeInfoAvailableNotification controleeInfoAvailable) { 87 if (CsmlUtil.isControleeInfoDo(controleeInfoAvailable.arbitraryData)) { 88 ControleeInfo controleeInfo = 89 ControleeInfo.fromBytes(TlvParser.parseOneTlv( 90 controleeInfoAvailable.arbitraryData).value); 91 generateAndPutSessionDataToApplet(controleeInfo); 92 } else { 93 logd("try to get ControleeInfo from applet."); 94 GetDoCommand getControleeInfoCommand = 95 GetDoCommand.build(CsmlUtil.constructGetDoTlv(CsmlUtil.CONTROLEE_INFO_DO_TAG)); 96 mFiRaSecureChannel.sendLocalFiRaCommand(getControleeInfoCommand, 97 new FiRaSecureChannel.ExternalRequestCallback() { 98 @Override 99 public void onSuccess(@NonNull byte[] responseData) { 100 ControleeInfo controleeInfo = 101 ControleeInfo.fromBytes( 102 TlvParser.parseOneTlv(responseData).value); 103 generateAndPutSessionDataToApplet(controleeInfo); 104 } 105 106 @Override 107 public void onFailure() { 108 logw("ControleeInfo is not available in applet."); 109 terminateSession(); 110 mSessionCallback.onSessionAborted(); 111 } 112 }); 113 } 114 } 115 generateAndPutSessionDataToApplet(@onNull ControleeInfo controleeInfo)116 private void generateAndPutSessionDataToApplet(@NonNull ControleeInfo controleeInfo) { 117 try { 118 mSessionData = CsmlUtil.generateSessionData( 119 mRunningProfileSessionInfo.uwbCapability, 120 controleeInfo, 121 mRunningProfileSessionInfo.sharedPrimarySessionId, 122 mRunningProfileSessionInfo.sharedPrimarySessionKeyInfo, 123 mUniqueSessionId.get(), 124 !mIsDefaultUniqueSessionId); 125 } catch (IllegalStateException e) { 126 logw("Uwb capability may not be compatible: " + e); 127 terminateSession(); 128 mSessionCallback.onSessionAborted(); 129 } 130 // put session data 131 132 PutDoCommand putSessionDataCommand = 133 PutDoCommand.build(CsmlUtil.constructGetOrPutDoTlv( 134 new TlvDatum(CsmlUtil.SESSION_DATA_DO_TAG, mSessionData.toBytes()))); 135 mFiRaSecureChannel.sendLocalFiRaCommand(putSessionDataCommand, 136 new FiRaSecureChannel.ExternalRequestCallback() { 137 @Override 138 public void onSuccess(@NonNull byte[] responseData) { 139 // do nothing, wait 'GetSessionData' command from remote. 140 } 141 142 @Override 143 public void onFailure() { 144 logw("failed to put session data to applet."); 145 terminateSession(); 146 mSessionCallback.onSessionAborted(); 147 } 148 }); 149 } 150 handleRdsAvailable(DispatchResponse.RdsAvailableNotification rdsAvailable)151 private void handleRdsAvailable(DispatchResponse.RdsAvailableNotification rdsAvailable) { 152 if (mSessionData == null) { 153 logw("session data is not available."); 154 terminateSession(); 155 mSessionCallback.onSessionAborted(); 156 return; 157 } 158 if (mUniqueSessionId.isPresent() && mUniqueSessionId.get() != rdsAvailable.sessionId) { 159 logw("unique session id was present, shouldn't be updated as " 160 + rdsAvailable.sessionId); 161 mUniqueSessionId = Optional.of(rdsAvailable.sessionId); 162 } 163 mSessionCallback.onSessionDataReady(mUniqueSessionId.get(), 164 Optional.of(mSessionData), /*isSessionTerminated=*/ false); 165 } 166 167 @Override onUnsolicitedDataToHostReceived(@onNull byte[] data)168 protected void onUnsolicitedDataToHostReceived(@NonNull byte[] data) { 169 logd("unsolicited data received: " + DataTypeConversionUtil.byteArrayToHexString(data)); 170 } 171 logd(@onNull String dbgMsg)172 private void logd(@NonNull String dbgMsg) { 173 Log.d(LOG_TAG, dbgMsg); 174 } 175 logw(@onNull String dbgMsg)176 private void logw(@NonNull String dbgMsg) { 177 Log.d(LOG_TAG, dbgMsg); 178 } 179 } 180