Home | History | Annotate | Download | only in usb
      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
     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      */
    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      */
    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
    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 
    267     public void onClick(DialogInterface dialog, int which) {
    268         if (which == AlertDialog.BUTTON_POSITIVE) {
    269             mPermissionGranted = true;
    270         }
    271         finish();
    272     }
    273 
    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