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 an 14 * limitations under the License. 15 */ 16 17 package com.android.server.usb; 18 19 import android.app.Notification; 20 import android.app.NotificationManager; 21 import android.app.PendingIntent; 22 import android.content.BroadcastReceiver; 23 import android.content.ComponentName; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.IntentFilter; 27 import android.content.pm.PackageManager; 28 import android.content.res.Resources; 29 import android.hardware.usb.UsbConstants; 30 import android.hardware.usb.UsbDevice; 31 import android.hardware.usb.UsbInterface; 32 import android.hardware.usb.UsbManager; 33 import android.os.UserHandle; 34 35 /** 36 * Manager for MTP storage notification. 37 */ 38 class MtpNotificationManager { 39 private static final String TAG = "UsbMtpNotificationManager"; 40 41 /** 42 * Subclass for PTP. 43 */ 44 private static final int SUBCLASS_STILL_IMAGE_CAPTURE = 1; 45 46 /** 47 * Subclass for Android style MTP. 48 */ 49 private static final int SUBCLASS_MTP = 0xff; 50 51 /** 52 * Protocol for Picture Transfer Protocol (PIMA 15470). 53 */ 54 private static final int PROTOCOL_PTP = 1; 55 56 /** 57 * Protocol for Android style MTP. 58 */ 59 private static final int PROTOCOL_MTP = 0; 60 61 private static final String ACTION_OPEN_IN_APPS = "com.android.server.usb.ACTION_OPEN_IN_APPS"; 62 63 private final Context mContext; 64 private final OnOpenInAppListener mListener; 65 66 MtpNotificationManager(Context context, OnOpenInAppListener listener) { 67 mContext = context; 68 mListener = listener; 69 final Receiver receiver = new Receiver(); 70 context.registerReceiver(receiver, new IntentFilter(ACTION_OPEN_IN_APPS)); 71 } 72 73 void showNotification(UsbDevice device) { 74 final Resources resources = mContext.getResources(); 75 final String title = resources.getString( 76 com.android.internal.R.string.usb_mtp_launch_notification_title, 77 device.getProductName()); 78 final String description = resources.getString( 79 com.android.internal.R.string.usb_mtp_launch_notification_description); 80 final Notification.Builder builder = new Notification.Builder(mContext) 81 .setContentTitle(title) 82 .setContentText(description) 83 .setSmallIcon(com.android.internal.R.drawable.stat_sys_data_usb) 84 .setCategory(Notification.CATEGORY_SYSTEM); 85 86 final Intent intent = new Intent(ACTION_OPEN_IN_APPS); 87 intent.putExtra(UsbManager.EXTRA_DEVICE, device); 88 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND); 89 90 final PendingIntent openIntent = PendingIntent.getBroadcastAsUser( 91 mContext, 92 device.getDeviceId(), 93 intent, 94 PendingIntent.FLAG_UPDATE_CURRENT, 95 UserHandle.SYSTEM); 96 builder.setContentIntent(openIntent); 97 98 final Notification notification = builder.build(); 99 notification.flags |= Notification.FLAG_LOCAL_ONLY; 100 101 mContext.getSystemService(NotificationManager.class).notify( 102 TAG, device.getDeviceId(), notification); 103 } 104 105 void hideNotification(int deviceId) { 106 mContext.getSystemService(NotificationManager.class).cancel(TAG, deviceId); 107 } 108 109 private class Receiver extends BroadcastReceiver { 110 @Override 111 public void onReceive(Context context, Intent intent) { 112 final UsbDevice device = 113 intent.getExtras().<UsbDevice>getParcelable(UsbManager.EXTRA_DEVICE); 114 if (device == null) { 115 return; 116 } 117 switch (intent.getAction()) { 118 case ACTION_OPEN_IN_APPS: 119 mListener.onOpenInApp(device); 120 break; 121 } 122 } 123 } 124 125 static boolean shouldShowNotification(PackageManager packageManager, UsbDevice device) { 126 // We don't show MTP notification for devices that has FEATURE_AUTOMOTIVE. 127 return !packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE) && 128 isMtpDevice(device); 129 } 130 131 private static boolean isMtpDevice(UsbDevice device) { 132 for (int i = 0; i < device.getInterfaceCount(); i++) { 133 final UsbInterface usbInterface = device.getInterface(i); 134 if ((usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_STILL_IMAGE && 135 usbInterface.getInterfaceSubclass() == SUBCLASS_STILL_IMAGE_CAPTURE && 136 usbInterface.getInterfaceProtocol() == PROTOCOL_PTP)) { 137 return true; 138 } 139 if (usbInterface.getInterfaceClass() == UsbConstants.USB_SUBCLASS_VENDOR_SPEC && 140 usbInterface.getInterfaceSubclass() == SUBCLASS_MTP && 141 usbInterface.getInterfaceProtocol() == PROTOCOL_MTP && 142 "MTP".equals(usbInterface.getName())) { 143 return true; 144 } 145 } 146 return false; 147 } 148 149 static interface OnOpenInAppListener { 150 void onOpenInApp(UsbDevice device); 151 } 152 } 153