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