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