• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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 android.car;
18 
19 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
20 
21 import android.annotation.IntDef;
22 import android.annotation.MainThread;
23 import android.annotation.NonNull;
24 import android.annotation.SystemApi;
25 import android.app.Service;
26 import android.content.Intent;
27 import android.hardware.usb.UsbDevice;
28 import android.os.Bundle;
29 import android.os.Handler;
30 import android.os.IBinder;
31 import android.os.Looper;
32 import android.os.Message;
33 import android.os.Messenger;
34 import android.os.RemoteException;
35 import android.util.Slog;
36 
37 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
38 
39 import java.io.FileDescriptor;
40 import java.io.PrintWriter;
41 import java.lang.annotation.Retention;
42 import java.lang.annotation.RetentionPolicy;
43 import java.lang.ref.WeakReference;
44 import java.util.Objects;
45 
46 /**
47  * The service that must be implemented by USB AOAP handler system apps. The app must hold the
48  * following permission: {@code android.car.permission.CAR_HANDLE_USB_AOAP_DEVICE}.
49  *
50  * <p>This service gets bound by the framework and the service needs to be protected by
51  * {@code android.permission.MANAGE_USB} permission to ensure nobody else can
52  * bind to the service. At most only one client should be bound at a time.
53  *
54  * @hide
55  */
56 @SystemApi
57 public abstract class AoapService extends Service {
58     private static final String TAG = AoapService.class.getSimpleName();
59 
60     /** Indicates success or confirmation. */
61     public static final int RESULT_OK = 0;
62 
63     /**
64      * Indicates that the device is not supported by this service and system shouldn't associate
65      * given device with this service.
66      */
67     public static final int RESULT_DEVICE_NOT_SUPPORTED = 1;
68 
69     /**
70      * Indicates that device shouldn't be switch to AOAP mode at this time.
71      */
72     public static final int RESULT_DO_NOT_SWITCH_TO_AOAP = 2;
73 
74     /** @hide */
75     @IntDef(value = {
76             RESULT_OK, RESULT_DEVICE_NOT_SUPPORTED, RESULT_DO_NOT_SWITCH_TO_AOAP
77     })
78     @Retention(RetentionPolicy.SOURCE)
79     public @interface Result {}
80 
81 
82     /**
83      * A message sent from the system USB handler service to AOAP handler service to check if the
84      * device is supported. The message must have {@link #KEY_DEVICE} with {@link UsbDevice} object.
85      *
86      * @hide
87      */
88     public static final int MSG_NEW_DEVICE_ATTACHED = 1;
89 
90     /**
91      * A response message for {@link #MSG_NEW_DEVICE_ATTACHED}. Must contain {@link #KEY_RESULT}
92      * with one of the {@code RESULT_*} constant.
93      *
94      * @hide */
95     public static final int MSG_NEW_DEVICE_ATTACHED_RESPONSE = 2;
96 
97     /**
98      * A message sent from the system USB handler service to AOAP handler service to check if the
99      * device can be switched to AOAP mode. The message must have {@link #KEY_DEVICE} with
100      * {@link UsbDevice} object.
101      *
102      * @hide
103      */
104     public static final int MSG_CAN_SWITCH_TO_AOAP = 3;
105 
106     /**
107      * A response message for {@link #MSG_CAN_SWITCH_TO_AOAP}. Must contain {@link #KEY_RESULT}
108      * with one of the {@code RESULT_*} constant.
109      *
110      * @hide */
111     public static final int MSG_CAN_SWITCH_TO_AOAP_RESPONSE = 4;
112 
113     /** @hide */
114     public static final String KEY_DEVICE = "usb-device";
115 
116     /** @hide */
117     public static final String KEY_RESULT = "result";
118 
119 
120     /**
121      * Returns {@code true} if the given USB device is supported by this service.
122      *
123      * <p>The device is not expected to be in AOAP mode when this method is called. The purpose of
124      * this method is just to give the service a chance to tell whether based on the information
125      * provided in {@link UsbDevice} class (e.g. PID/VID) this service supports or doesn't support
126      * given device.
127      *
128      * <p>The method must return one of the following status: {@link #RESULT_OK} or
129      * {@link #RESULT_DEVICE_NOT_SUPPORTED}
130      */
131     @MainThread
isDeviceSupported(@onNull UsbDevice device)132     public abstract @Result int isDeviceSupported(@NonNull UsbDevice device);
133 
134     /**
135      * This method will be called at least once per connection session before switching device into
136      * AOAP mode.
137      *
138      * <p>The device is connected, but not in AOAP mode yet. Implementors of this method may ask
139      * the framework to ignore this device for now and do not switch to AOAP. This may make sense if
140      * a connection to the device has been established through other means, and switching the device
141      * to AOAP would break that connection.
142      *
143      * <p>Note: the method may be called only if this device was claimed to be supported in
144      * {@link #isDeviceSupported(UsbDevice)} method, and this app has been chosen to handle the
145      * device.
146      *
147      * <p>The method must return one of the following status: {@link #RESULT_OK},
148      * {@link #RESULT_DEVICE_NOT_SUPPORTED} or {@link #RESULT_DO_NOT_SWITCH_TO_AOAP}
149      */
150     @MainThread
canSwitchToAoap(@onNull UsbDevice device)151     public @Result int canSwitchToAoap(@NonNull UsbDevice device) {
152         return RESULT_OK;
153     }
154 
155     private Messenger mMessenger;
156     private boolean mBound;
157 
158     @Override
onCreate()159     public void onCreate() {
160         super.onCreate();
161         mMessenger = new Messenger(new IncomingHandler(this));
162     }
163 
164     @Override
onBind(Intent intent)165     public IBinder onBind(Intent intent) {
166         if (mBound) {
167             Slog.w(TAG, "Received onBind event when the service was already bound");
168         }
169         mBound = true;
170         return mMessenger.getBinder();
171     }
172 
173     @Override
onUnbind(Intent intent)174     public boolean onUnbind(Intent intent) {
175         mBound = false;
176         return super.onUnbind(intent);
177     }
178 
179     @Override
180     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dump(FileDescriptor fd, PrintWriter writer, String[] args)181     protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
182         writer.write("Bound: " + mBound);
183     }
184 
185     private static class IncomingHandler extends Handler {
186         private final WeakReference<AoapService> mServiceRef;
187 
IncomingHandler(AoapService service)188         IncomingHandler(AoapService service) {
189             super(Looper.getMainLooper());
190             mServiceRef = new WeakReference<>(service);
191         }
192 
193         @Override
handleMessage(Message msg)194         public void handleMessage(Message msg) {
195             AoapService service = mServiceRef.get();
196             if (service == null) {
197                 return;
198             }
199             Bundle data = msg.getData();
200             if (data == null) {
201                 Slog.e(TAG, "Ignoring message " + msg.what + " without data");
202                 return;
203             }
204 
205             Slog.i(TAG, "Message received: " + msg.what);
206 
207             switch (msg.what) {
208                 case MSG_NEW_DEVICE_ATTACHED: {
209                     int res = service.isDeviceSupported(
210                             Objects.requireNonNull(data.getParcelable(KEY_DEVICE)));
211                     if (res != RESULT_OK && res != RESULT_DEVICE_NOT_SUPPORTED) {
212                         throw new IllegalArgumentException("Result can not be " + res);
213                     }
214                     sendResponse(msg.replyTo, MSG_NEW_DEVICE_ATTACHED_RESPONSE, res);
215                     break;
216                 }
217 
218                 case MSG_CAN_SWITCH_TO_AOAP: {
219                     int res = service.canSwitchToAoap(
220                             Objects.requireNonNull(data.getParcelable(KEY_DEVICE)));
221                     if (res != RESULT_OK && res != RESULT_DEVICE_NOT_SUPPORTED
222                             && res != RESULT_DO_NOT_SWITCH_TO_AOAP) {
223                         throw new IllegalArgumentException("Result can not be " + res);
224                     }
225                     sendResponse(msg.replyTo, MSG_CAN_SWITCH_TO_AOAP_RESPONSE, res);
226                     break;
227                 }
228 
229                 default:
230                     Slog.e(TAG, "Unknown message received: " + msg.what);
231                     break;
232             }
233         }
234 
sendResponse(Messenger messenger, int msg, int result)235         private void sendResponse(Messenger messenger, int msg, int result) {
236             try {
237                 messenger.send(createResponseMessage(msg, result));
238             } catch (RemoteException e) {
239                 Slog.e(TAG, "Failed to send message", e);
240             }
241         }
242 
createResponseMessage(int msg, int result)243         private Message createResponseMessage(int msg, int result) {
244             Message response = Message.obtain(null, msg);
245             Bundle data = new Bundle();
246             data.putInt(KEY_RESULT, result);
247             response.setData(data);
248             return response;
249         }
250     }
251 }
252