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_RDS_AVAILABLE; 20 21 import android.os.Looper; 22 import android.util.Log; 23 24 import androidx.annotation.NonNull; 25 26 import com.android.server.uwb.pm.RunningProfileSessionInfo; 27 import com.android.server.uwb.secure.csml.CsmlUtil; 28 import com.android.server.uwb.secure.csml.DispatchResponse; 29 import com.android.server.uwb.secure.csml.GetDoCommand; 30 import com.android.server.uwb.secure.csml.PutDoCommand; 31 import com.android.server.uwb.secure.csml.SessionData; 32 import com.android.server.uwb.secure.iso7816.TlvDatum; 33 import com.android.server.uwb.secure.iso7816.TlvParser; 34 import com.android.server.uwb.util.DataTypeConversionUtil; 35 36 import java.util.Optional; 37 38 /** 39 * The responder of dynamic STS session managed by the UWB controlee. 40 */ 41 public class ControleeResponderSession extends ResponderSession { 42 private static final String LOG_TAG = "ControleeResponder"; 43 ControleeResponderSession( @onNull Looper workLooper, @NonNull FiRaSecureChannel fiRaSecureChannel, @NonNull Callback sessionCallback, @NonNull RunningProfileSessionInfo runningProfileSessionInfo)44 public ControleeResponderSession( 45 @NonNull Looper workLooper, 46 @NonNull FiRaSecureChannel fiRaSecureChannel, 47 @NonNull Callback sessionCallback, 48 @NonNull RunningProfileSessionInfo runningProfileSessionInfo) { 49 super(workLooper, fiRaSecureChannel, sessionCallback, runningProfileSessionInfo); 50 } 51 52 @Override handleFiRaSecureChannelEstablished()53 protected void handleFiRaSecureChannelEstablished() { 54 super.handleFiRaSecureChannelEstablished(); 55 56 PutDoCommand putControleeInfoCommand = PutDoCommand.build( 57 CsmlUtil.constructGetOrPutDoTlv( 58 new TlvDatum(CsmlUtil.CONTROLEE_INFO_DO_TAG, 59 mRunningProfileSessionInfo.controleeInfo.get().toBytes()))); 60 mFiRaSecureChannel.sendLocalFiRaCommand(putControleeInfoCommand, 61 new FiRaSecureChannel.ExternalRequestCallback() { 62 @Override 63 public void onSuccess(@NonNull byte[] responseData) { 64 logd("controlee info is sent to applet."); 65 } 66 67 @Override 68 public void onFailure() { 69 logw("failed to send controlee info to applet."); 70 terminateSession(); 71 mSessionCallback.onSessionAborted(); 72 } 73 }); 74 } 75 76 @Override onDispatchResponseReceived(@onNull DispatchResponse dispatchResponse)77 protected boolean onDispatchResponseReceived(@NonNull DispatchResponse dispatchResponse) { 78 DispatchResponse.RdsAvailableNotification rdsAvailable = null; 79 for (DispatchResponse.Notification notification : dispatchResponse.notifications) { 80 switch (notification.notificationEventId) { 81 case NOTIFICATION_EVENT_ID_RDS_AVAILABLE: 82 // Responder notification 83 rdsAvailable = (DispatchResponse.RdsAvailableNotification) notification; 84 break; 85 default: 86 logw( 87 "Unexpected notification from dispatch response: " 88 + notification.notificationEventId); 89 } 90 } 91 if (rdsAvailable != null) { 92 if (mIsDefaultUniqueSessionId && mUniqueSessionId.get() != rdsAvailable.sessionId) { 93 logw("The default session Id is changed, which is not expected."); 94 } 95 mUniqueSessionId = Optional.of(rdsAvailable.sessionId); 96 97 if (rdsAvailable.arbitraryData.isPresent() 98 && CsmlUtil.isSessionDataDo(rdsAvailable.arbitraryData.get())) { 99 logd("SessionData is in RDS available notification."); 100 mSessionData = SessionData.fromBytes( 101 TlvParser.parseOneTlv(rdsAvailable.arbitraryData.get()).value); 102 mSessionCallback.onSessionDataReady( 103 mUniqueSessionId.get(), 104 Optional.of(mSessionData), 105 /* isSessionTerminated= */ false); 106 } else { 107 logd("try to read SessionData in applet."); 108 GetDoCommand getSessionDataCommand = 109 GetDoCommand.build(CsmlUtil.constructSessionDataGetDoTlv()); 110 mFiRaSecureChannel.sendLocalFiRaCommand(getSessionDataCommand, 111 new FiRaSecureChannel.ExternalRequestCallback() { 112 @Override 113 public void onSuccess(byte[] responseData) { 114 logd("success to get session data from local FiRa applet."); 115 if (!CsmlUtil.isSessionDataDo(responseData)) { 116 logw("session data is expected from applet."); 117 terminateSession(); 118 mSessionCallback.onSessionAborted(); 119 return; 120 } 121 mSessionData = SessionData.fromBytes( 122 TlvParser.parseOneTlv(responseData).value); 123 mSessionCallback.onSessionDataReady( 124 mUniqueSessionId.get(), 125 Optional.of(mSessionData), 126 /* isSessionTerminated= */ false); 127 } 128 129 @Override 130 public void onFailure() { 131 logw("failed to get session data from applet."); 132 terminateSession(); 133 mSessionCallback.onSessionAborted(); 134 } 135 }); 136 } 137 138 return true; 139 } 140 return false; 141 } 142 143 @Override onUnsolicitedDataToHostReceived(@onNull byte[] data)144 protected void onUnsolicitedDataToHostReceived(@NonNull byte[] data) { 145 logd("unsolicited data received: " + DataTypeConversionUtil.byteArrayToHexString(data)); 146 } 147 logd(@onNull String dbgMsg)148 private void logd(@NonNull String dbgMsg) { 149 Log.d(LOG_TAG, dbgMsg); 150 } 151 logw(@onNull String dbgMsg)152 private void logw(@NonNull String dbgMsg) { 153 Log.d(LOG_TAG, dbgMsg); 154 } 155 } 156