blob: 96f12cf85a3bd0f025476d7b4d89ff755e27dc54 [file] [log] [blame]
Kevin Crossand4285492016-11-28 18:40:43 -08001/*
2 * Copyright (C) 2016 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 */
16package android.car.usb.handler;
17
Pavel Maltsev6b49b9b2019-03-14 10:14:24 -070018import static android.content.pm.PackageManager.PERMISSION_GRANTED;
19
20import android.Manifest;
Nicholas Sauer3a3a4922018-09-05 07:50:03 -070021import android.annotation.Nullable;
Kevin Crossand4285492016-11-28 18:40:43 -080022import android.content.ComponentName;
23import android.content.Context;
24import android.content.Intent;
Kevin Crossand4285492016-11-28 18:40:43 -080025import android.content.pm.ActivityInfo;
26import android.content.pm.PackageManager;
27import android.content.pm.PackageManager.NameNotFoundException;
28import android.content.pm.ResolveInfo;
29import android.content.res.XmlResourceParser;
30import android.hardware.usb.UsbDevice;
31import android.hardware.usb.UsbDeviceConnection;
Kevin Crossand4285492016-11-28 18:40:43 -080032import android.hardware.usb.UsbManager;
33import android.os.Handler;
34import android.os.HandlerThread;
Kevin Crossand4285492016-11-28 18:40:43 -080035import android.os.Looper;
36import android.os.Message;
Kevin Crossand4285492016-11-28 18:40:43 -080037import android.util.Log;
Nicholas Sauer3a3a4922018-09-05 07:50:03 -070038
Kevin Crossand4285492016-11-28 18:40:43 -080039import com.android.internal.util.XmlUtils;
Nicholas Sauer3a3a4922018-09-05 07:50:03 -070040
41import org.xmlpull.v1.XmlPullParser;
42
Kevin Crossand4285492016-11-28 18:40:43 -080043import java.io.IOException;
44import java.util.ArrayList;
Kevin Crossand4285492016-11-28 18:40:43 -080045import java.util.List;
Kevin Crossand4285492016-11-28 18:40:43 -080046
47/** Resolves supported handlers for USB device. */
48public final class UsbDeviceHandlerResolver {
Pavel Maltsev6b49b9b2019-03-14 10:14:24 -070049
Kevin Crossand4285492016-11-28 18:40:43 -080050 private static final String TAG = UsbDeviceHandlerResolver.class.getSimpleName();
51 private static final boolean LOCAL_LOGD = true;
52
Pavel Maltsev6b49b9b2019-03-14 10:14:24 -070053 private static final String AOAP_HANDLE_PERMISSION =
54 "android.car.permission.CAR_HANDLE_USB_AOAP_DEVICE";
55
Kevin Crossand4285492016-11-28 18:40:43 -080056 /**
57 * Callbacks for device resolver.
58 */
59 public interface UsbDeviceHandlerResolverCallback {
60 /** Handlers are resolved */
61 void onHandlersResolveCompleted(
62 UsbDevice device, List<UsbDeviceSettings> availableSettings);
63 /** Device was dispatched */
64 void onDeviceDispatched();
65 }
66
67 private final UsbManager mUsbManager;
68 private final PackageManager mPackageManager;
69 private final UsbDeviceHandlerResolverCallback mDeviceCallback;
70 private final Context mContext;
71 private final HandlerThread mHandlerThread;
72 private final UsbDeviceResolverHandler mHandler;
Pavel Maltsev6b49b9b2019-03-14 10:14:24 -070073 private final AoapServiceManager mAoapServiceManager;
Kevin Crossand4285492016-11-28 18:40:43 -080074
75 public UsbDeviceHandlerResolver(UsbManager manager, Context context,
76 UsbDeviceHandlerResolverCallback deviceListener) {
77 mUsbManager = manager;
78 mContext = context;
79 mDeviceCallback = deviceListener;
80 mHandlerThread = new HandlerThread(TAG);
81 mHandlerThread.start();
82 mHandler = new UsbDeviceResolverHandler(mHandlerThread.getLooper());
83 mPackageManager = context.getPackageManager();
Pavel Maltsev6b49b9b2019-03-14 10:14:24 -070084 mAoapServiceManager = new AoapServiceManager(mContext.getApplicationContext());
Kevin Crossand4285492016-11-28 18:40:43 -080085 }
86
87 /**
88 * Releases current object.
89 */
90 public void release() {
91 if (mHandlerThread != null) {
92 mHandlerThread.quitSafely();
93 }
94 }
95
96 /**
97 * Resolves handlers for USB device.
98 */
99 public void resolve(UsbDevice device) {
100 mHandler.requestResolveHandlers(device);
101 }
102
103 /**
104 * Dispatches device to component.
105 */
106 public boolean dispatch(UsbDevice device, ComponentName component, boolean inAoap) {
107 if (LOCAL_LOGD) {
108 Log.d(TAG, "dispatch: " + device + " component: " + component + " inAoap: " + inAoap);
109 }
110
111 ActivityInfo activityInfo;
112 try {
113 activityInfo = mPackageManager.getActivityInfo(component, PackageManager.GET_META_DATA);
114 } catch (NameNotFoundException e) {
115 Log.e(TAG, "Activity not found: " + component);
116 return false;
117 }
118
119 Intent intent = createDeviceAttachedIntent(device);
120 if (inAoap) {
121 if (AoapInterface.isDeviceInAoapMode(device)) {
122 mDeviceCallback.onDeviceDispatched();
123 } else {
Pavel Maltsev6b49b9b2019-03-14 10:14:24 -0700124 UsbDeviceFilter filter =
Kevin Crossand4285492016-11-28 18:40:43 -0800125 packageMatches(activityInfo, intent.getAction(), device, true);
Pavel Maltsev6b49b9b2019-03-14 10:14:24 -0700126
Kevin Crossand4285492016-11-28 18:40:43 -0800127 if (filter != null) {
Pavel Maltsev6b49b9b2019-03-14 10:14:24 -0700128 mHandlerThread.getThreadHandler().post(() -> {
129 if (mAoapServiceManager.canSwitchDeviceToAoap(device,
130 ComponentName.unflattenFromString(filter.mAoapService))) {
131 requestAoapSwitch(device, filter);
132 } else {
133 Log.i(TAG, "Ignore AOAP switch for device " + device
134 + " handled by " + filter.mAoapService);
135 }
136 });
Kevin Crossand4285492016-11-28 18:40:43 -0800137 return true;
138 }
139 }
140 }
141
142 intent.setComponent(component);
143 mUsbManager.grantPermission(device, activityInfo.applicationInfo.uid);
144
145 mContext.startActivity(intent);
146 mHandler.requestCompleteDeviceDispatch();
147 return true;
148 }
149
150 private static Intent createDeviceAttachedIntent(UsbDevice device) {
151 Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED);
152 intent.putExtra(UsbManager.EXTRA_DEVICE, device);
153 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
154 return intent;
155 }
156
157 private void doHandleResolveHandlers(UsbDevice device) {
158 if (LOCAL_LOGD) {
159 Log.d(TAG, "doHandleResolveHandlers: " + device);
160 }
161
162 Intent intent = createDeviceAttachedIntent(device);
Pavel Maltsev6b49b9b2019-03-14 10:14:24 -0700163 List<UsbHandlerPackage> matches = getDeviceMatches(device, intent, false);
Kevin Crossand4285492016-11-28 18:40:43 -0800164 if (LOCAL_LOGD) {
165 Log.d(TAG, "matches size: " + matches.size());
166 }
Pavel Maltsev6b49b9b2019-03-14 10:14:24 -0700167 List<UsbDeviceSettings> settings = new ArrayList<>();
168 for (UsbHandlerPackage pkg : matches) {
169 settings.add(createSettings(device, pkg));
Kevin Crossand4285492016-11-28 18:40:43 -0800170 }
Kevin Crossand4285492016-11-28 18:40:43 -0800171
Pavel Maltsev6b49b9b2019-03-14 10:14:24 -0700172 UsbDeviceConnection devConnection = UsbUtil.openConnection(mUsbManager, device);
173 if (devConnection != null && AoapInterface.isSupported(mContext, device, devConnection)) {
174 for (UsbHandlerPackage pkg : getDeviceMatches(device, intent, true)) {
175 if (mAoapServiceManager.isDeviceSupported(device, pkg.mAoapService)) {
176 settings.add(createSettings(device, pkg));
177 }
Kevin Crossand4285492016-11-28 18:40:43 -0800178 }
Kevin Crossand4285492016-11-28 18:40:43 -0800179 }
Pavel Maltsev6b49b9b2019-03-14 10:14:24 -0700180
181 deviceProbingComplete(device, settings);
Kevin Crossand4285492016-11-28 18:40:43 -0800182 }
183
Pavel Maltsev6b49b9b2019-03-14 10:14:24 -0700184 private UsbDeviceSettings createSettings(UsbDevice device, UsbHandlerPackage pkg) {
185 UsbDeviceSettings settings = UsbDeviceSettings.constructSettings(device);
186 settings.setHandler(pkg.mActivity);
187 settings.setAoap(pkg.mAoapService != null);
188 return settings;
189 }
190
191 private void requestAoapSwitch(UsbDevice device, UsbDeviceFilter filter) {
Kevin Crossand4285492016-11-28 18:40:43 -0800192 UsbDeviceConnection connection = UsbUtil.openConnection(mUsbManager, device);
Nicholas Sauer3a3a4922018-09-05 07:50:03 -0700193 if (connection == null) {
194 Log.e(TAG, "Failed to connect to usb device.");
195 return;
196 }
Kevin Crossand4285492016-11-28 18:40:43 -0800197 try {
198 UsbUtil.sendAoapAccessoryStart(
199 connection,
200 filter.mAoapManufacturer,
201 filter.mAoapModel,
202 filter.mAoapDescription,
203 filter.mAoapVersion,
204 filter.mAoapUri,
205 filter.mAoapSerial);
206 } catch (IOException e) {
207 Log.w(TAG, "Failed to switch device into AOAP mode", e);
208 }
209 connection.close();
210 }
211
Pavel Maltsev6b49b9b2019-03-14 10:14:24 -0700212 private void deviceProbingComplete(UsbDevice device, List<UsbDeviceSettings> settings) {
Kevin Crossand4285492016-11-28 18:40:43 -0800213 if (LOCAL_LOGD) {
214 Log.d(TAG, "deviceProbingComplete");
215 }
Pavel Maltsev6b49b9b2019-03-14 10:14:24 -0700216 mDeviceCallback.onHandlersResolveCompleted(device, settings);
Kevin Crossand4285492016-11-28 18:40:43 -0800217 }
218
Pavel Maltsev6b49b9b2019-03-14 10:14:24 -0700219 private List<UsbHandlerPackage> getDeviceMatches(
Kevin Crossand4285492016-11-28 18:40:43 -0800220 UsbDevice device, Intent intent, boolean forAoap) {
Pavel Maltsev6b49b9b2019-03-14 10:14:24 -0700221 List<UsbHandlerPackage> matches = new ArrayList<>();
Kevin Crossand4285492016-11-28 18:40:43 -0800222 List<ResolveInfo> resolveInfos =
223 mPackageManager.queryIntentActivities(intent, PackageManager.GET_META_DATA);
224 for (ResolveInfo resolveInfo : resolveInfos) {
Pavel Maltsev6b49b9b2019-03-14 10:14:24 -0700225 if (forAoap && !hasAoapPermission(resolveInfo.resolvePackageName)) {
226 Log.w(TAG, "Package " + resolveInfo.resolvePackageName + " does not hold "
227 + AOAP_HANDLE_PERMISSION + " permission. Ignore the package.");
228 continue;
229 }
230
231 UsbDeviceFilter filter = packageMatches(resolveInfo.activityInfo,
Kevin Crossand4285492016-11-28 18:40:43 -0800232 intent.getAction(), device, forAoap);
233 if (filter != null) {
Pavel Maltsev6b49b9b2019-03-14 10:14:24 -0700234 ActivityInfo ai = resolveInfo.activityInfo;
235 ComponentName activity = new ComponentName(ai.packageName, ai.name);
236 ComponentName aoapService = filter.mAoapService == null
237 ? null : ComponentName.unflattenFromString(filter.mAoapService);
238
239 if (aoapService != null && !checkServiceRequiresPermission(aoapService)) {
240 continue;
241 }
242
243 if (aoapService != null || !forAoap) {
244 matches.add(new UsbHandlerPackage(activity, aoapService));
245 }
Kevin Crossand4285492016-11-28 18:40:43 -0800246 }
247 }
248 return matches;
249 }
250
Pavel Maltsev6b49b9b2019-03-14 10:14:24 -0700251 private boolean checkServiceRequiresPermission(ComponentName serviceName) {
252 Intent intent = new Intent();
253 intent.setComponent(serviceName);
254 boolean found = false;
255 for (ResolveInfo info : mPackageManager.queryIntentServices(intent, 0)) {
256 if (info.serviceInfo != null) {
257 found = true;
258 if ((Manifest.permission.MANAGE_USB.equals(info.serviceInfo.permission))) {
259 return true;
260 }
261 }
262 }
263 if (found) {
264 Log.w(TAG, "Component " + serviceName + " must be protected with "
265 + Manifest.permission.MANAGE_USB + " permission");
266 } else {
267 Log.w(TAG, "Component " + serviceName + " not found");
268 }
269 return false;
270 }
271
272 private boolean hasAoapPermission(String packageName) {
273 return mPackageManager
274 .checkPermission(AOAP_HANDLE_PERMISSION, packageName) == PERMISSION_GRANTED;
275 }
276
277 private UsbDeviceFilter packageMatches(ActivityInfo ai, String metaDataName, UsbDevice device,
Kevin Crossand4285492016-11-28 18:40:43 -0800278 boolean forAoap) {
279 if (LOCAL_LOGD) {
280 Log.d(TAG, "packageMatches ai: " + ai + "metaDataName: " + metaDataName + " forAoap: "
281 + forAoap);
282 }
283 String filterTagName = forAoap ? "usb-aoap-accessory" : "usb-device";
Pavel Maltsev6b49b9b2019-03-14 10:14:24 -0700284 try (XmlResourceParser parser = ai.loadXmlMetaData(mPackageManager, metaDataName)) {
Kevin Crossand4285492016-11-28 18:40:43 -0800285 if (parser == null) {
286 Log.w(TAG, "no meta-data for " + ai);
287 return null;
288 }
289
290 XmlUtils.nextElement(parser);
291 while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
292 String tagName = parser.getName();
293 if (device != null && filterTagName.equals(tagName)) {
Pavel Maltsev6b49b9b2019-03-14 10:14:24 -0700294 UsbDeviceFilter filter = UsbDeviceFilter.read(parser, forAoap);
Kevin Crossand4285492016-11-28 18:40:43 -0800295 if (forAoap || filter.matches(device)) {
296 return filter;
297 }
298 }
299 XmlUtils.nextElement(parser);
300 }
301 } catch (Exception e) {
302 Log.w(TAG, "Unable to load component info " + ai.toString(), e);
Kevin Crossand4285492016-11-28 18:40:43 -0800303 }
304 return null;
305 }
306
307 private class UsbDeviceResolverHandler extends Handler {
308 private static final int MSG_RESOLVE_HANDLERS = 0;
Kevin Crossand4285492016-11-28 18:40:43 -0800309 private static final int MSG_COMPLETE_DISPATCH = 3;
310
Kevin Crossand4285492016-11-28 18:40:43 -0800311 private UsbDeviceResolverHandler(Looper looper) {
312 super(looper);
313 }
314
Pavel Maltsev6b49b9b2019-03-14 10:14:24 -0700315 void requestResolveHandlers(UsbDevice device) {
Kevin Crossand4285492016-11-28 18:40:43 -0800316 Message msg = obtainMessage(MSG_RESOLVE_HANDLERS, device);
317 sendMessage(msg);
318 }
319
Pavel Maltsev6b49b9b2019-03-14 10:14:24 -0700320 void requestCompleteDeviceDispatch() {
Kevin Crossand4285492016-11-28 18:40:43 -0800321 sendEmptyMessage(MSG_COMPLETE_DISPATCH);
322 }
323
324 @Override
325 public void handleMessage(Message msg) {
326 switch (msg.what) {
327 case MSG_RESOLVE_HANDLERS:
328 doHandleResolveHandlers((UsbDevice) msg.obj);
329 break;
Kevin Crossand4285492016-11-28 18:40:43 -0800330 case MSG_COMPLETE_DISPATCH:
331 mDeviceCallback.onDeviceDispatched();
332 break;
333 default:
334 Log.w(TAG, "Unsupported message: " + msg);
335 }
336 }
337 }
Pavel Maltsev6b49b9b2019-03-14 10:14:24 -0700338
339 private static class UsbHandlerPackage {
340 final ComponentName mActivity;
341 final @Nullable ComponentName mAoapService;
342
343 UsbHandlerPackage(ComponentName activity, @Nullable ComponentName aoapService) {
344 mActivity = activity;
345 mAoapService = aoapService;
346 }
347 }
Kevin Crossand4285492016-11-28 18:40:43 -0800348}