1 /* 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 */ 16 package android.car.usb.handler; 17 18 import static android.content.pm.PackageManager.PERMISSION_GRANTED; 19 20 import android.Manifest; 21 import android.annotation.Nullable; 22 import android.content.ComponentName; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.pm.ActivityInfo; 26 import android.content.pm.PackageManager; 27 import android.content.pm.PackageManager.NameNotFoundException; 28 import android.content.pm.ResolveInfo; 29 import android.content.res.XmlResourceParser; 30 import android.hardware.usb.UsbDevice; 31 import android.hardware.usb.UsbDeviceConnection; 32 import android.hardware.usb.UsbManager; 33 import android.os.Handler; 34 import android.os.HandlerThread; 35 import android.os.Looper; 36 import android.os.Message; 37 import android.util.Log; 38 39 import com.android.internal.util.XmlUtils; 40 41 import org.xmlpull.v1.XmlPullParser; 42 43 import java.io.IOException; 44 import java.util.ArrayList; 45 import java.util.List; 46 47 /** Resolves supported handlers for USB device. */ 48 public final class UsbDeviceHandlerResolver { 49 50 private static final String TAG = UsbDeviceHandlerResolver.class.getSimpleName(); 51 private static final boolean LOCAL_LOGD = true; 52 53 private static final String AOAP_HANDLE_PERMISSION = 54 "android.car.permission.CAR_HANDLE_USB_AOAP_DEVICE"; 55 56 /** 57 * Callbacks for device resolver. 58 */ 59 public interface UsbDeviceHandlerResolverCallback { 60 /** Handlers are resolved */ onHandlersResolveCompleted( UsbDevice device, List<UsbDeviceSettings> availableSettings)61 void onHandlersResolveCompleted( 62 UsbDevice device, List<UsbDeviceSettings> availableSettings); 63 /** Device was dispatched */ onDeviceDispatched()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; 73 private final AoapServiceManager mAoapServiceManager; 74 UsbDeviceHandlerResolver(UsbManager manager, Context context, UsbDeviceHandlerResolverCallback deviceListener)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(); 84 mAoapServiceManager = new AoapServiceManager(mContext.getApplicationContext()); 85 } 86 87 /** 88 * Releases current object. 89 */ release()90 public void release() { 91 if (mHandlerThread != null) { 92 mHandlerThread.quitSafely(); 93 } 94 } 95 96 /** 97 * Resolves handlers for USB device. 98 */ resolve(UsbDevice device)99 public void resolve(UsbDevice device) { 100 mHandler.requestResolveHandlers(device); 101 } 102 103 /** 104 * Dispatches device to component. 105 */ dispatch(UsbDevice device, ComponentName component, boolean inAoap)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 { 124 UsbDeviceFilter filter = 125 packageMatches(activityInfo, intent.getAction(), device, true); 126 127 if (filter != null) { 128 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 }); 137 mDeviceCallback.onDeviceDispatched(); 138 return true; 139 } 140 } 141 } 142 143 intent.setComponent(component); 144 mUsbManager.grantPermission(device, activityInfo.applicationInfo.uid); 145 146 mContext.startActivity(intent); 147 mHandler.requestCompleteDeviceDispatch(); 148 return true; 149 } 150 createDeviceAttachedIntent(UsbDevice device)151 private static Intent createDeviceAttachedIntent(UsbDevice device) { 152 Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED); 153 intent.putExtra(UsbManager.EXTRA_DEVICE, device); 154 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 155 return intent; 156 } 157 doHandleResolveHandlers(UsbDevice device)158 private void doHandleResolveHandlers(UsbDevice device) { 159 if (LOCAL_LOGD) { 160 Log.d(TAG, "doHandleResolveHandlers: " + device); 161 } 162 163 Intent intent = createDeviceAttachedIntent(device); 164 List<UsbHandlerPackage> matches = getDeviceMatches(device, intent, false); 165 if (LOCAL_LOGD) { 166 Log.d(TAG, "matches size: " + matches.size()); 167 } 168 List<UsbDeviceSettings> settings = new ArrayList<>(); 169 for (UsbHandlerPackage pkg : matches) { 170 settings.add(createSettings(device, pkg)); 171 } 172 173 UsbDeviceConnection devConnection = UsbUtil.openConnection(mUsbManager, device); 174 if (devConnection != null && AoapInterface.isSupported(mContext, device, devConnection)) { 175 for (UsbHandlerPackage pkg : getDeviceMatches(device, intent, true)) { 176 if (mAoapServiceManager.isDeviceSupported(device, pkg.mAoapService)) { 177 settings.add(createSettings(device, pkg)); 178 } 179 } 180 } 181 182 deviceProbingComplete(device, settings); 183 } 184 createSettings(UsbDevice device, UsbHandlerPackage pkg)185 private UsbDeviceSettings createSettings(UsbDevice device, UsbHandlerPackage pkg) { 186 UsbDeviceSettings settings = UsbDeviceSettings.constructSettings(device); 187 settings.setHandler(pkg.mActivity); 188 settings.setAoap(pkg.mAoapService != null); 189 return settings; 190 } 191 requestAoapSwitch(UsbDevice device, UsbDeviceFilter filter)192 private void requestAoapSwitch(UsbDevice device, UsbDeviceFilter filter) { 193 UsbDeviceConnection connection = UsbUtil.openConnection(mUsbManager, device); 194 if (connection == null) { 195 Log.e(TAG, "Failed to connect to usb device."); 196 return; 197 } 198 try { 199 UsbUtil.sendAoapAccessoryStart( 200 connection, 201 filter.mAoapManufacturer, 202 filter.mAoapModel, 203 filter.mAoapDescription, 204 filter.mAoapVersion, 205 filter.mAoapUri, 206 filter.mAoapSerial); 207 } catch (IOException e) { 208 Log.w(TAG, "Failed to switch device into AOAP mode", e); 209 } 210 connection.close(); 211 } 212 deviceProbingComplete(UsbDevice device, List<UsbDeviceSettings> settings)213 private void deviceProbingComplete(UsbDevice device, List<UsbDeviceSettings> settings) { 214 if (LOCAL_LOGD) { 215 Log.d(TAG, "deviceProbingComplete"); 216 } 217 mDeviceCallback.onHandlersResolveCompleted(device, settings); 218 } 219 getDeviceMatches( UsbDevice device, Intent intent, boolean forAoap)220 private List<UsbHandlerPackage> getDeviceMatches( 221 UsbDevice device, Intent intent, boolean forAoap) { 222 List<UsbHandlerPackage> matches = new ArrayList<>(); 223 List<ResolveInfo> resolveInfos = 224 mPackageManager.queryIntentActivities(intent, PackageManager.GET_META_DATA); 225 for (ResolveInfo resolveInfo : resolveInfos) { 226 final String packageName = resolveInfo.activityInfo.packageName; 227 if (forAoap && !hasAoapPermission(packageName)) { 228 Log.w(TAG, "Package " + packageName + " does not hold " 229 + AOAP_HANDLE_PERMISSION + " permission. Ignore the package."); 230 continue; 231 } 232 233 UsbDeviceFilter filter = packageMatches(resolveInfo.activityInfo, 234 intent.getAction(), device, forAoap); 235 if (filter != null) { 236 ActivityInfo ai = resolveInfo.activityInfo; 237 ComponentName activity = new ComponentName(ai.packageName, ai.name); 238 ComponentName aoapService = filter.mAoapService == null 239 ? null : ComponentName.unflattenFromString(filter.mAoapService); 240 241 if (aoapService != null && !checkServiceRequiresPermission(aoapService)) { 242 continue; 243 } 244 245 if (aoapService != null || !forAoap) { 246 matches.add(new UsbHandlerPackage(activity, aoapService)); 247 } 248 } 249 } 250 return matches; 251 } 252 checkServiceRequiresPermission(ComponentName serviceName)253 private boolean checkServiceRequiresPermission(ComponentName serviceName) { 254 Intent intent = new Intent(); 255 intent.setComponent(serviceName); 256 boolean found = false; 257 for (ResolveInfo info : mPackageManager.queryIntentServices(intent, 0)) { 258 if (info.serviceInfo != null) { 259 found = true; 260 if ((Manifest.permission.MANAGE_USB.equals(info.serviceInfo.permission))) { 261 return true; 262 } 263 } 264 } 265 if (found) { 266 Log.w(TAG, "Component " + serviceName + " must be protected with " 267 + Manifest.permission.MANAGE_USB + " permission"); 268 } else { 269 Log.w(TAG, "Component " + serviceName + " not found"); 270 } 271 return false; 272 } 273 hasAoapPermission(String packageName)274 private boolean hasAoapPermission(String packageName) { 275 return mPackageManager 276 .checkPermission(AOAP_HANDLE_PERMISSION, packageName) == PERMISSION_GRANTED; 277 } 278 packageMatches(ActivityInfo ai, String metaDataName, UsbDevice device, boolean forAoap)279 private UsbDeviceFilter packageMatches(ActivityInfo ai, String metaDataName, UsbDevice device, 280 boolean forAoap) { 281 if (LOCAL_LOGD) { 282 Log.d(TAG, "packageMatches ai: " + ai + "metaDataName: " + metaDataName + " forAoap: " 283 + forAoap); 284 } 285 String filterTagName = forAoap ? "usb-aoap-accessory" : "usb-device"; 286 try (XmlResourceParser parser = ai.loadXmlMetaData(mPackageManager, metaDataName)) { 287 if (parser == null) { 288 Log.w(TAG, "no meta-data for " + ai); 289 return null; 290 } 291 292 XmlUtils.nextElement(parser); 293 while (parser.getEventType() != XmlPullParser.END_DOCUMENT) { 294 String tagName = parser.getName(); 295 if (device != null && filterTagName.equals(tagName)) { 296 UsbDeviceFilter filter = UsbDeviceFilter.read(parser, forAoap); 297 if (forAoap || filter.matches(device)) { 298 return filter; 299 } 300 } 301 XmlUtils.nextElement(parser); 302 } 303 } catch (Exception e) { 304 Log.w(TAG, "Unable to load component info " + ai.toString(), e); 305 } 306 return null; 307 } 308 309 private class UsbDeviceResolverHandler extends Handler { 310 private static final int MSG_RESOLVE_HANDLERS = 0; 311 private static final int MSG_COMPLETE_DISPATCH = 3; 312 UsbDeviceResolverHandler(Looper looper)313 private UsbDeviceResolverHandler(Looper looper) { 314 super(looper); 315 } 316 requestResolveHandlers(UsbDevice device)317 void requestResolveHandlers(UsbDevice device) { 318 Message msg = obtainMessage(MSG_RESOLVE_HANDLERS, device); 319 sendMessage(msg); 320 } 321 requestCompleteDeviceDispatch()322 void requestCompleteDeviceDispatch() { 323 sendEmptyMessage(MSG_COMPLETE_DISPATCH); 324 } 325 326 @Override handleMessage(Message msg)327 public void handleMessage(Message msg) { 328 switch (msg.what) { 329 case MSG_RESOLVE_HANDLERS: 330 doHandleResolveHandlers((UsbDevice) msg.obj); 331 break; 332 case MSG_COMPLETE_DISPATCH: 333 mDeviceCallback.onDeviceDispatched(); 334 break; 335 default: 336 Log.w(TAG, "Unsupported message: " + msg); 337 } 338 } 339 } 340 341 private static class UsbHandlerPackage { 342 final ComponentName mActivity; 343 final @Nullable ComponentName mAoapService; 344 UsbHandlerPackage(ComponentName activity, @Nullable ComponentName aoapService)345 UsbHandlerPackage(ComponentName activity, @Nullable ComponentName aoapService) { 346 mActivity = activity; 347 mAoapService = aoapService; 348 } 349 } 350 } 351