1 /* 2 * Copyright (C) 2011 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 com.android.systemui.usb; 18 19 import android.annotation.NonNull; 20 import android.app.AlertDialog; 21 import android.app.PendingIntent; 22 import android.content.Context; 23 import android.content.DialogInterface; 24 import android.content.Intent; 25 import android.content.pm.ActivityInfo; 26 import android.content.pm.ApplicationInfo; 27 import android.content.pm.PackageInfo; 28 import android.content.pm.PackageManager; 29 import android.content.res.XmlResourceParser; 30 import android.hardware.usb.IUsbManager; 31 import android.hardware.usb.UsbAccessory; 32 import android.hardware.usb.UsbDevice; 33 import android.hardware.usb.UsbManager; 34 import android.os.Bundle; 35 import android.os.IBinder; 36 import android.os.RemoteException; 37 import android.os.ServiceManager; 38 import android.os.UserHandle; 39 import android.util.Log; 40 import android.view.LayoutInflater; 41 import android.view.View; 42 import android.widget.CheckBox; 43 import android.widget.CompoundButton; 44 import android.widget.TextView; 45 46 import com.android.internal.app.AlertActivity; 47 import com.android.internal.app.AlertController; 48 import com.android.internal.util.XmlUtils; 49 import android.hardware.usb.AccessoryFilter; 50 import android.hardware.usb.DeviceFilter; 51 import com.android.systemui.R; 52 53 import org.xmlpull.v1.XmlPullParser; 54 55 public class UsbPermissionActivity extends AlertActivity 56 implements DialogInterface.OnClickListener, CheckBox.OnCheckedChangeListener { 57 58 private static final String TAG = "UsbPermissionActivity"; 59 60 private CheckBox mAlwaysUse; 61 private TextView mClearDefaultHint; 62 private UsbDevice mDevice; 63 private UsbAccessory mAccessory; 64 private PendingIntent mPendingIntent; 65 private String mPackageName; 66 private int mUid; 67 private boolean mPermissionGranted; 68 private UsbDisconnectedReceiver mDisconnectedReceiver; 69 70 @Override onCreate(Bundle icicle)71 public void onCreate(Bundle icicle) { 72 super.onCreate(icicle); 73 74 Intent intent = getIntent(); 75 mDevice = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); 76 mAccessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY); 77 mPendingIntent = (PendingIntent)intent.getParcelableExtra(Intent.EXTRA_INTENT); 78 mUid = intent.getIntExtra(Intent.EXTRA_UID, -1); 79 mPackageName = intent.getStringExtra("package"); 80 81 PackageManager packageManager = getPackageManager(); 82 ApplicationInfo aInfo; 83 try { 84 aInfo = packageManager.getApplicationInfo(mPackageName, 0); 85 } catch (PackageManager.NameNotFoundException e) { 86 Log.e(TAG, "unable to look up package name", e); 87 finish(); 88 return; 89 } 90 String appName = aInfo.loadLabel(packageManager).toString(); 91 92 final AlertController.AlertParams ap = mAlertParams; 93 ap.mTitle = appName; 94 if (mDevice == null) { 95 ap.mMessage = getString(R.string.usb_accessory_permission_prompt, appName, 96 mAccessory.getDescription()); 97 mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mAccessory); 98 } else { 99 ap.mMessage = getString(R.string.usb_device_permission_prompt, appName, 100 mDevice.getProductName()); 101 mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mDevice); 102 } 103 ap.mPositiveButtonText = getString(android.R.string.ok); 104 ap.mNegativeButtonText = getString(android.R.string.cancel); 105 ap.mPositiveButtonListener = this; 106 ap.mNegativeButtonListener = this; 107 108 try { 109 PackageInfo packageInfo = packageManager.getPackageInfo(mPackageName, 110 PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA); 111 112 if ((mDevice != null && canBeDefault(mDevice, packageInfo)) 113 || (mAccessory != null && canBeDefault(mAccessory, packageInfo))) { 114 // add "open when" checkbox 115 LayoutInflater inflater = (LayoutInflater) getSystemService( 116 Context.LAYOUT_INFLATER_SERVICE); 117 ap.mView = inflater.inflate(com.android.internal.R.layout.always_use_checkbox, null); 118 mAlwaysUse = (CheckBox) ap.mView.findViewById(com.android.internal.R.id.alwaysUse); 119 if (mDevice == null) { 120 mAlwaysUse.setText(getString(R.string.always_use_accessory, appName, 121 mAccessory.getDescription())); 122 } else { 123 mAlwaysUse.setText(getString(R.string.always_use_device, appName, 124 mDevice.getProductName())); 125 } 126 mAlwaysUse.setOnCheckedChangeListener(this); 127 128 mClearDefaultHint = (TextView)ap.mView.findViewById( 129 com.android.internal.R.id.clearDefaultHint); 130 mClearDefaultHint.setVisibility(View.GONE); 131 } 132 } catch (PackageManager.NameNotFoundException e) { 133 // ignore 134 } 135 136 setupAlert(); 137 138 } 139 140 /** 141 * Can the app be the default for the USB device. I.e. can the app be launched by default if 142 * the device is plugged in. 143 * 144 * @param device The device the app would be default for 145 * @param packageInfo The package info of the app 146 * 147 * @return {@code true} iff the app can be default 148 */ canBeDefault(@onNull UsbDevice device, @NonNull PackageInfo packageInfo)149 private boolean canBeDefault(@NonNull UsbDevice device, @NonNull PackageInfo packageInfo) { 150 ActivityInfo[] activities = packageInfo.activities; 151 if (activities != null) { 152 int numActivities = activities.length; 153 for (int i = 0; i < numActivities; i++) { 154 ActivityInfo activityInfo = activities[i]; 155 156 try (XmlResourceParser parser = activityInfo.loadXmlMetaData(getPackageManager(), 157 UsbManager.ACTION_USB_DEVICE_ATTACHED)) { 158 if (parser == null) { 159 continue; 160 } 161 162 XmlUtils.nextElement(parser); 163 while (parser.getEventType() != XmlPullParser.END_DOCUMENT) { 164 if ("usb-device".equals(parser.getName())) { 165 DeviceFilter filter = DeviceFilter.read(parser); 166 if (filter.matches(device)) { 167 return true; 168 } 169 } 170 171 XmlUtils.nextElement(parser); 172 } 173 } catch (Exception e) { 174 Log.w(TAG, "Unable to load component info " + activityInfo.toString(), e); 175 } 176 } 177 } 178 179 return false; 180 } 181 182 /** 183 * Can the app be the default for the USB accessory. I.e. can the app be launched by default if 184 * the accessory is plugged in. 185 * 186 * @param accessory The accessory the app would be default for 187 * @param packageInfo The package info of the app 188 * 189 * @return {@code true} iff the app can be default 190 */ canBeDefault(@onNull UsbAccessory accessory, @NonNull PackageInfo packageInfo)191 private boolean canBeDefault(@NonNull UsbAccessory accessory, 192 @NonNull PackageInfo packageInfo) { 193 ActivityInfo[] activities = packageInfo.activities; 194 if (activities != null) { 195 int numActivities = activities.length; 196 for (int i = 0; i < numActivities; i++) { 197 ActivityInfo activityInfo = activities[i]; 198 199 try (XmlResourceParser parser = activityInfo.loadXmlMetaData(getPackageManager(), 200 UsbManager.ACTION_USB_ACCESSORY_ATTACHED)) { 201 if (parser == null) { 202 continue; 203 } 204 205 XmlUtils.nextElement(parser); 206 while (parser.getEventType() != XmlPullParser.END_DOCUMENT) { 207 if ("usb-accessory".equals(parser.getName())) { 208 AccessoryFilter filter = AccessoryFilter.read(parser); 209 if (filter.matches(accessory)) { 210 return true; 211 } 212 } 213 214 XmlUtils.nextElement(parser); 215 } 216 } catch (Exception e) { 217 Log.w(TAG, "Unable to load component info " + activityInfo.toString(), e); 218 } 219 } 220 } 221 222 return false; 223 } 224 225 @Override onDestroy()226 public void onDestroy() { 227 IBinder b = ServiceManager.getService(USB_SERVICE); 228 IUsbManager service = IUsbManager.Stub.asInterface(b); 229 230 // send response via pending intent 231 Intent intent = new Intent(); 232 try { 233 if (mDevice != null) { 234 intent.putExtra(UsbManager.EXTRA_DEVICE, mDevice); 235 if (mPermissionGranted) { 236 service.grantDevicePermission(mDevice, mUid); 237 if (mAlwaysUse != null && mAlwaysUse.isChecked()) { 238 final int userId = UserHandle.getUserId(mUid); 239 service.setDevicePackage(mDevice, mPackageName, userId); 240 } 241 } 242 } 243 if (mAccessory != null) { 244 intent.putExtra(UsbManager.EXTRA_ACCESSORY, mAccessory); 245 if (mPermissionGranted) { 246 service.grantAccessoryPermission(mAccessory, mUid); 247 if (mAlwaysUse != null && mAlwaysUse.isChecked()) { 248 final int userId = UserHandle.getUserId(mUid); 249 service.setAccessoryPackage(mAccessory, mPackageName, userId); 250 } 251 } 252 } 253 intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, mPermissionGranted); 254 mPendingIntent.send(this, 0, intent); 255 } catch (PendingIntent.CanceledException e) { 256 Log.w(TAG, "PendingIntent was cancelled"); 257 } catch (RemoteException e) { 258 Log.e(TAG, "IUsbService connection failed", e); 259 } 260 261 if (mDisconnectedReceiver != null) { 262 unregisterReceiver(mDisconnectedReceiver); 263 } 264 super.onDestroy(); 265 } 266 onClick(DialogInterface dialog, int which)267 public void onClick(DialogInterface dialog, int which) { 268 if (which == AlertDialog.BUTTON_POSITIVE) { 269 mPermissionGranted = true; 270 } 271 finish(); 272 } 273 onCheckedChanged(CompoundButton buttonView, boolean isChecked)274 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 275 if (mClearDefaultHint == null) return; 276 277 if(isChecked) { 278 mClearDefaultHint.setVisibility(View.VISIBLE); 279 } else { 280 mClearDefaultHint.setVisibility(View.GONE); 281 } 282 } 283 } 284