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 package android.hardware.usb; 17 18 import android.annotation.CallbackExecutor; 19 import android.annotation.IntDef; 20 import android.hardware.usb.IUsbOperationInternal; 21 import android.hardware.usb.UsbPort; 22 import android.util.Log; 23 24 import java.lang.annotation.Retention; 25 import java.lang.annotation.RetentionPolicy; 26 import java.util.concurrent.locks.Condition; 27 import java.util.concurrent.locks.ReentrantLock; 28 import java.util.concurrent.Executor; 29 import java.util.concurrent.TimeUnit; 30 import java.util.function.Consumer; 31 /** 32 * UsbOperationInternal allows UsbPort to support both synchronous and 33 * asynchronous function irrespective of whether the underlying hal 34 * method is synchronous or asynchronous. 35 * 36 * @hide 37 */ 38 public final class UsbOperationInternal extends IUsbOperationInternal.Stub { 39 private static final String TAG = "UsbPortStatus"; 40 private final int mOperationID; 41 // Cached portId. 42 private final String mId; 43 // True implies operation did not timeout. 44 private boolean mOperationComplete; 45 private boolean mAsynchronous = false; 46 private Executor mExecutor; 47 private Consumer<Integer> mConsumer; 48 private int mResult = 0; 49 private @UsbOperationStatus int mStatus; 50 final ReentrantLock mLock = new ReentrantLock(); 51 final Condition mOperationWait = mLock.newCondition(); 52 // Maximum time the caller has to wait for onOperationComplete to be called. 53 private static final int USB_OPERATION_TIMEOUT_MSECS = 5000; 54 55 /** 56 * The requested operation was successfully completed. 57 * Returned in {@link onOperationComplete} and {@link getStatus}. 58 */ 59 public static final int USB_OPERATION_SUCCESS = 0; 60 61 /** 62 * The requested operation failed due to internal error. 63 * Returned in {@link onOperationComplete} and {@link getStatus}. 64 */ 65 public static final int USB_OPERATION_ERROR_INTERNAL = 1; 66 67 /** 68 * The requested operation failed as it's not supported. 69 * Returned in {@link onOperationComplete} and {@link getStatus}. 70 */ 71 public static final int USB_OPERATION_ERROR_NOT_SUPPORTED = 2; 72 73 /** 74 * The requested operation failed as it's not supported. 75 * Returned in {@link onOperationComplete} and {@link getStatus}. 76 */ 77 public static final int USB_OPERATION_ERROR_PORT_MISMATCH = 3; 78 79 @IntDef(prefix = { "USB_OPERATION_" }, value = { 80 USB_OPERATION_SUCCESS, 81 USB_OPERATION_ERROR_INTERNAL, 82 USB_OPERATION_ERROR_NOT_SUPPORTED, 83 USB_OPERATION_ERROR_PORT_MISMATCH 84 }) 85 @Retention(RetentionPolicy.SOURCE) 86 @interface UsbOperationStatus{} 87 UsbOperationInternal(int operationID, String id, Executor executor, Consumer<Integer> consumer)88 UsbOperationInternal(int operationID, String id, 89 Executor executor, Consumer<Integer> consumer) { 90 this.mOperationID = operationID; 91 this.mId = id; 92 this.mExecutor = executor; 93 this.mConsumer = consumer; 94 this.mAsynchronous = true; 95 } 96 UsbOperationInternal(int operationID, String id)97 UsbOperationInternal(int operationID, String id) { 98 this.mOperationID = operationID; 99 this.mId = id; 100 } 101 102 /** 103 * Hal glue layer would directly call this function when the requested 104 * operation is complete. 105 */ 106 @Override onOperationComplete(@sbOperationStatus int status)107 public void onOperationComplete(@UsbOperationStatus int status) { 108 mLock.lock(); 109 try { 110 mOperationComplete = true; 111 mStatus = status; 112 Log.i(TAG, "Port:" + mId + " opID:" + mOperationID + " status:" + mStatus); 113 if (mAsynchronous) { 114 switch (mStatus) { 115 case USB_OPERATION_SUCCESS: 116 mResult = UsbPort.RESET_USB_PORT_SUCCESS; 117 break; 118 case USB_OPERATION_ERROR_INTERNAL: 119 mResult = UsbPort.RESET_USB_PORT_ERROR_INTERNAL; 120 break; 121 case USB_OPERATION_ERROR_NOT_SUPPORTED: 122 mResult = UsbPort.RESET_USB_PORT_ERROR_NOT_SUPPORTED; 123 break; 124 case USB_OPERATION_ERROR_PORT_MISMATCH: 125 mResult = UsbPort.RESET_USB_PORT_ERROR_PORT_MISMATCH; 126 break; 127 default: 128 mResult = UsbPort.RESET_USB_PORT_ERROR_OTHER; 129 } 130 mExecutor.execute(() -> mConsumer.accept(mResult)); 131 } else { 132 mOperationWait.signal(); 133 } 134 } finally { 135 mLock.unlock(); 136 } 137 } 138 139 /** 140 * Caller invokes this function to wait for the operation to be complete. 141 */ waitForOperationComplete()142 public void waitForOperationComplete() { 143 mLock.lock(); 144 try { 145 long now = System.currentTimeMillis(); 146 long deadline = now + USB_OPERATION_TIMEOUT_MSECS; 147 // Wait in loop to overcome spurious wakeups. 148 do { 149 mOperationWait.await(deadline - System.currentTimeMillis(), 150 TimeUnit.MILLISECONDS); 151 } while (!mOperationComplete && System.currentTimeMillis() < deadline); 152 if (!mOperationComplete) { 153 Log.e(TAG, "Port:" + mId + " opID:" + mOperationID 154 + " operationComplete not received in " + USB_OPERATION_TIMEOUT_MSECS 155 + "msecs"); 156 } 157 } catch (InterruptedException e) { 158 Log.e(TAG, "Port:" + mId + " opID:" + mOperationID + " operationComplete interrupted"); 159 } finally { 160 mLock.unlock(); 161 } 162 } 163 getStatus()164 public @UsbOperationStatus int getStatus() { 165 return mOperationComplete ? mStatus : USB_OPERATION_ERROR_INTERNAL; 166 } 167 } 168