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 
MtpNotificationManager(Context context, OnOpenInAppListener listener)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 
showNotification(UsbDevice device)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 
hideNotification(int deviceId)105     void hideNotification(int deviceId) {
106         mContext.getSystemService(NotificationManager.class).cancel(TAG, deviceId);
107     }
108 
109     private class Receiver extends BroadcastReceiver {
110         @Override
onReceive(Context context, Intent intent)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 
shouldShowNotification(PackageManager packageManager, UsbDevice device)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 
isMtpDevice(UsbDevice device)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 {
onOpenInApp(UsbDevice device)150         void onOpenInApp(UsbDevice device);
151     }
152 }
153