/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions an * limitations under the License. */ package com.android.server.usb; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.res.Resources; import android.hardware.usb.UsbConstants; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbInterface; import android.hardware.usb.UsbManager; import android.os.UserHandle; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; /** * Manager for MTP storage notification. */ class MtpNotificationManager { private static final String TAG = "UsbMtpNotificationManager"; /** * Subclass for PTP. */ private static final int SUBCLASS_STILL_IMAGE_CAPTURE = 1; /** * Subclass for Android style MTP. */ private static final int SUBCLASS_MTP = 0xff; /** * Protocol for Picture Transfer Protocol (PIMA 15470). */ private static final int PROTOCOL_PTP = 1; /** * Protocol for Android style MTP. */ private static final int PROTOCOL_MTP = 0; private static final String ACTION_OPEN_IN_APPS = "com.android.server.usb.ACTION_OPEN_IN_APPS"; private final Context mContext; private final OnOpenInAppListener mListener; MtpNotificationManager(Context context, OnOpenInAppListener listener) { mContext = context; mListener = listener; final Receiver receiver = new Receiver(); context.registerReceiver(receiver, new IntentFilter(ACTION_OPEN_IN_APPS)); } void showNotification(UsbDevice device) { final Resources resources = mContext.getResources(); final String title = resources.getString( com.android.internal.R.string.usb_mtp_launch_notification_title, device.getProductName()); final String description = resources.getString( com.android.internal.R.string.usb_mtp_launch_notification_description); final Notification.Builder builder = new Notification.Builder(mContext, SystemNotificationChannels.USB) .setContentTitle(title) .setContentText(description) .setSmallIcon(com.android.internal.R.drawable.stat_sys_data_usb) .setCategory(Notification.CATEGORY_SYSTEM); final Intent intent = new Intent(ACTION_OPEN_IN_APPS); intent.putExtra(UsbManager.EXTRA_DEVICE, device); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND); final PendingIntent openIntent = PendingIntent.getBroadcastAsUser( mContext, device.getDeviceId(), intent, PendingIntent.FLAG_UPDATE_CURRENT, UserHandle.SYSTEM); builder.setContentIntent(openIntent); final Notification notification = builder.build(); notification.flags |= Notification.FLAG_LOCAL_ONLY; mContext.getSystemService(NotificationManager.class).notify( Integer.toString(device.getDeviceId()), SystemMessage.NOTE_USB_MTP_TAP, notification); } void hideNotification(int deviceId) { mContext.getSystemService(NotificationManager.class).cancel( Integer.toString(deviceId), SystemMessage.NOTE_USB_MTP_TAP); } private class Receiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { final UsbDevice device = intent.getExtras().getParcelable(UsbManager.EXTRA_DEVICE); if (device == null) { return; } switch (intent.getAction()) { case ACTION_OPEN_IN_APPS: mListener.onOpenInApp(device); break; } } } static boolean shouldShowNotification(PackageManager packageManager, UsbDevice device) { // We don't show MTP notification for devices that has FEATURE_AUTOMOTIVE. return !packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE) && isMtpDevice(device); } private static boolean isMtpDevice(UsbDevice device) { for (int i = 0; i < device.getInterfaceCount(); i++) { final UsbInterface usbInterface = device.getInterface(i); if ((usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_STILL_IMAGE && usbInterface.getInterfaceSubclass() == SUBCLASS_STILL_IMAGE_CAPTURE && usbInterface.getInterfaceProtocol() == PROTOCOL_PTP)) { return true; } if (usbInterface.getInterfaceClass() == UsbConstants.USB_SUBCLASS_VENDOR_SPEC && usbInterface.getInterfaceSubclass() == SUBCLASS_MTP && usbInterface.getInterfaceProtocol() == PROTOCOL_MTP && "MTP".equals(usbInterface.getName())) { return true; } } return false; } static interface OnOpenInAppListener { void onOpenInApp(UsbDevice device); } }