• 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 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