Add eventsSupported property to MtpDeviceInfo.

The property provides the set of event code that are supported by
MtpDevice.

BUG=26147375

Change-Id: I54be75e4bb52ddfe9aba8630538ddd32d1a641c8
diff --git a/api/current.txt b/api/current.txt
index b89a17d..03b25f7 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -22632,6 +22632,7 @@
   }
 
   public class MtpDeviceInfo {
+    method public final int[] getEventsSupported();
     method public final java.lang.String getManufacturer();
     method public final java.lang.String getModel();
     method public final int[] getOperationsSupported();
diff --git a/api/system-current.txt b/api/system-current.txt
index 664bcd8..c69f6b4 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -24190,6 +24190,7 @@
   }
 
   public class MtpDeviceInfo {
+    method public final int[] getEventsSupported();
     method public final java.lang.String getManufacturer();
     method public final java.lang.String getModel();
     method public final int[] getOperationsSupported();
diff --git a/api/test-current.txt b/api/test-current.txt
index 55468e2..1c68e22 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -22640,6 +22640,7 @@
   }
 
   public class MtpDeviceInfo {
+    method public final int[] getEventsSupported();
     method public final java.lang.String getManufacturer();
     method public final java.lang.String getModel();
     method public final int[] getOperationsSupported();
diff --git a/media/java/android/mtp/MtpDeviceInfo.java b/media/java/android/mtp/MtpDeviceInfo.java
index 1ceca84..2e4f451 100644
--- a/media/java/android/mtp/MtpDeviceInfo.java
+++ b/media/java/android/mtp/MtpDeviceInfo.java
@@ -30,6 +30,7 @@
     private String mVersion;
     private String mSerialNumber;
     private int[] mOperationsSupported;
+    private int[] mEventsSupported;
 
     // only instantiated via JNI
     private MtpDeviceInfo() {
@@ -74,9 +75,71 @@
     /**
      * Returns operation code supported by the device.
      *
-     * @return supported operation code
+     * @return supported operation code. Can be null if device does not provide the property.
+     * @see MtpConstants#OPERATION_GET_DEVICE_INFO
+     * @see MtpConstants#OPERATION_OPEN_SESSION
+     * @see MtpConstants#OPERATION_CLOSE_SESSION
+     * @see MtpConstants#OPERATION_GET_STORAGE_I_DS
+     * @see MtpConstants#OPERATION_GET_STORAGE_INFO
+     * @see MtpConstants#OPERATION_GET_NUM_OBJECTS
+     * @see MtpConstants#OPERATION_GET_OBJECT_HANDLES
+     * @see MtpConstants#OPERATION_GET_OBJECT_INFO
+     * @see MtpConstants#OPERATION_GET_OBJECT
+     * @see MtpConstants#OPERATION_GET_THUMB
+     * @see MtpConstants#OPERATION_DELETE_OBJECT
+     * @see MtpConstants#OPERATION_SEND_OBJECT_INFO
+     * @see MtpConstants#OPERATION_SEND_OBJECT
+     * @see MtpConstants#OPERATION_INITIATE_CAPTURE
+     * @see MtpConstants#OPERATION_FORMAT_STORE
+     * @see MtpConstants#OPERATION_RESET_DEVICE
+     * @see MtpConstants#OPERATION_SELF_TEST
+     * @see MtpConstants#OPERATION_SET_OBJECT_PROTECTION
+     * @see MtpConstants#OPERATION_POWER_DOWN
+     * @see MtpConstants#OPERATION_GET_DEVICE_PROP_DESC
+     * @see MtpConstants#OPERATION_GET_DEVICE_PROP_VALUE
+     * @see MtpConstants#OPERATION_SET_DEVICE_PROP_VALUE
+     * @see MtpConstants#OPERATION_RESET_DEVICE_PROP_VALUE
+     * @see MtpConstants#OPERATION_TERMINATE_OPEN_CAPTURE
+     * @see MtpConstants#OPERATION_MOVE_OBJECT
+     * @see MtpConstants#OPERATION_COPY_OBJECT
+     * @see MtpConstants#OPERATION_GET_PARTIAL_OBJECT
+     * @see MtpConstants#OPERATION_INITIATE_OPEN_CAPTURE
+     * @see MtpConstants#OPERATION_GET_OBJECT_PROPS_SUPPORTED
+     * @see MtpConstants#OPERATION_GET_OBJECT_PROP_DESC
+     * @see MtpConstants#OPERATION_GET_OBJECT_PROP_VALUE
+     * @see MtpConstants#OPERATION_SET_OBJECT_PROP_VALUE
+     * @see MtpConstants#OPERATION_GET_OBJECT_REFERENCES
+     * @see MtpConstants#OPERATION_SET_OBJECT_REFERENCES
+     * @see MtpConstants#OPERATION_SKIP
      */
     public final @Nullable int[] getOperationsSupported() {
         return mOperationsSupported;
     }
+
+    /**
+     * Returns event code supported by the device.
+     *
+     * @return supported event code. Can be null if device does not provide the property.
+     * @see MtpConstants#EVENT_UNDEFINED
+     * @see MtpConstants#EVENT_CANCEL_TRANSACTION
+     * @see MtpConstants#EVENT_OBJECT_ADDED
+     * @see MtpConstants#EVENT_OBJECT_REMOVED
+     * @see MtpConstants#EVENT_STORE_ADDED
+     * @see MtpConstants#EVENT_STORE_REMOVED
+     * @see MtpConstants#EVENT_DEVICE_PROP_CHANGED
+     * @see MtpConstants#EVENT_OBJECT_INFO_CHANGED
+     * @see MtpConstants#EVENT_DEVICE_INFO_CHANGED
+     * @see MtpConstants#EVENT_REQUEST_OBJECT_TRANSFER
+     * @see MtpConstants#EVENT_STORE_FULL
+     * @see MtpConstants#EVENT_DEVICE_RESET
+     * @see MtpConstants#EVENT_STORAGE_INFO_CHANGED
+     * @see MtpConstants#EVENT_CAPTURE_COMPLETE
+     * @see MtpConstants#EVENT_UNREPORTED_STATUS
+     * @see MtpConstants#EVENT_OBJECT_PROP_CHANGED
+     * @see MtpConstants#EVENT_OBJECT_PROP_DESC_CHANGED
+     * @see MtpConstants#EVENT_OBJECT_REFERENCES_CHANGED
+     */
+    public final @Nullable int[] getEventsSupported() {
+        return mEventsSupported;
+    }
 }
diff --git a/media/jni/android_mtp_MtpDevice.cpp b/media/jni/android_mtp_MtpDevice.cpp
index b1b3b62..8b7a926 100644
--- a/media/jni/android_mtp_MtpDevice.cpp
+++ b/media/jni/android_mtp_MtpDevice.cpp
@@ -25,6 +25,8 @@
 #include <unistd.h>
 #include <fcntl.h>
 
+#include <memory>
+
 #include "jni.h"
 #include "JNIHelp.h"
 #include "ScopedPrimitiveArray.h"
@@ -66,6 +68,7 @@
 static jfieldID field_deviceInfo_version;
 static jfieldID field_deviceInfo_serialNumber;
 static jfieldID field_deviceInfo_operationsSupported;
+static jfieldID field_deviceInfo_eventsSupported;
 
 // MtpStorageInfo fields
 static jfieldID field_storageInfo_storageId;
@@ -216,7 +219,7 @@
         ALOGD("android_mtp_MtpDevice_get_device_info device is null");
         return NULL;
     }
-    MtpDeviceInfo* deviceInfo = device->getDeviceInfo();
+    std::unique_ptr<MtpDeviceInfo> deviceInfo(device->getDeviceInfo());
     if (!deviceInfo) {
         ALOGD("android_mtp_MtpDevice_get_device_info deviceInfo is null");
         return NULL;
@@ -224,7 +227,6 @@
     jobject info = env->NewObject(clazz_deviceInfo, constructor_deviceInfo);
     if (info == NULL) {
         ALOGE("Could not create a MtpDeviceInfo object");
-        delete deviceInfo;
         return NULL;
     }
 
@@ -242,17 +244,35 @@
             env->NewStringUTF(deviceInfo->mSerial));
     if (deviceInfo->mOperations) {
         const size_t size = deviceInfo->mOperations->size();
-        const jintArray operations = env->NewIntArray(size);
+        ScopedLocalRef<jintArray> operations(env, static_cast<jintArray>(env->NewIntArray(size)));
         {
-            ScopedIntArrayRW elements(env, operations);
+            ScopedIntArrayRW elements(env, operations.get());
+            if (elements.get() == NULL) {
+                ALOGE("Could not create operationsSupported element.");
+                return NULL;
+            }
             for (size_t i = 0; i < size; ++i) {
                 elements[i] = deviceInfo->mOperations->itemAt(i);
             }
+            env->SetObjectField(info, field_deviceInfo_operationsSupported, operations.get());
         }
-        env->SetObjectField(info, field_deviceInfo_operationsSupported, operations);
+    }
+    if (deviceInfo->mEvents) {
+        const size_t size = deviceInfo->mEvents->size();
+        ScopedLocalRef<jintArray> events(env, static_cast<jintArray>(env->NewIntArray(size)));
+        {
+            ScopedIntArrayRW elements(env, events.get());
+            if (elements.get() == NULL) {
+                ALOGE("Could not create eventsSupported element.");
+                return NULL;
+            }
+            for (size_t i = 0; i < size; ++i) {
+                elements[i] = deviceInfo->mEvents->itemAt(i);
+            }
+            env->SetObjectField(info, field_deviceInfo_eventsSupported, events.get());
+        }
     }
 
-    delete deviceInfo;
     return info;
 }
 
@@ -686,6 +706,11 @@
         ALOGE("Can't find MtpDeviceInfo.mOperationsSupported");
         return -1;
     }
+    field_deviceInfo_eventsSupported = env->GetFieldID(clazz, "mEventsSupported", "[I");
+    if (field_deviceInfo_eventsSupported == NULL) {
+        ALOGE("Can't find MtpDeviceInfo.mEventsSupported");
+        return -1;
+    }
     clazz_deviceInfo = (jclass)env->NewGlobalRef(clazz);
 
     clazz = env->FindClass("android/mtp/MtpStorageInfo");
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDeviceRecord.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDeviceRecord.java
index 02d07b9..fa99a38 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDeviceRecord.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDeviceRecord.java
@@ -16,19 +16,23 @@
 
 package com.android.mtp;
 
+import android.annotation.Nullable;
+
 class MtpDeviceRecord {
     public final int deviceId;
     public final String name;
     public final boolean opened;
     public final MtpRoot[] roots;
-    public final int[] operationsSupported;
+    public final @Nullable int[] operationsSupported;
+    public final @Nullable int[] eventsSupported;
 
-    MtpDeviceRecord(
-            int deviceId, String name, boolean opened, MtpRoot[] roots, int[] operationsSupported) {
+    MtpDeviceRecord(int deviceId, String name, boolean opened, MtpRoot[] roots,
+                    @Nullable int[] operationsSupported, @Nullable int[] eventsSupported) {
         this.deviceId = deviceId;
         this.name = name;
         this.opened = opened;
         this.roots = roots;
         this.operationsSupported = operationsSupported;
+        this.eventsSupported = eventsSupported;
     }
 }
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
index 0d81a30..efe5ff1 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
@@ -129,6 +129,7 @@
             final String name = device.getProductName();
             MtpRoot[] roots;
             int[] operationsSupported = null;
+            int[] eventsSupported = null;
             if (opened) {
                 try {
                     roots = getRoots(device.getDeviceId());
@@ -142,16 +143,14 @@
                 final MtpDeviceInfo info = mtpDevice.getDeviceInfo();
                 if (info != null) {
                     operationsSupported = mtpDevice.getDeviceInfo().getOperationsSupported();
-                }
-                if (operationsSupported == null) {
-                    operationsSupported = new int[0];
+                    eventsSupported = mtpDevice.getDeviceInfo().getEventsSupported();
                 }
             } else {
                 roots = new MtpRoot[0];
-                operationsSupported = new int[0];
             }
             devices.add(new MtpDeviceRecord(
-                    device.getDeviceId(), name, opened, roots, operationsSupported));
+                    device.getDeviceId(), name, opened, roots, operationsSupported,
+                    eventsSupported));
         }
         return devices.toArray(new MtpDeviceRecord[devices.size()]);
     }
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
index c39d5b3..97ea717 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
@@ -77,7 +77,7 @@
     public void testPutSingleStorageDocuments() throws Exception {
         mDatabase.getMapper().startAddingDocuments(null);
         mDatabase.getMapper().putDeviceDocument(
-                new MtpDeviceRecord(0, "Device", true, new MtpRoot[0], new int[0]));
+                new MtpDeviceRecord(0, "Device", true, new MtpRoot[0], null, null));
         mDatabase.getMapper().stopAddingDocuments(null);
 
         mDatabase.getMapper().startAddingDocuments("1");
@@ -425,9 +425,9 @@
         };
         mDatabase.getMapper().startAddingDocuments(null);
         mDatabase.getMapper().putDeviceDocument(
-                new MtpDeviceRecord(0, "Device A", true, new MtpRoot[0], new int[0]));
+                new MtpDeviceRecord(0, "Device A", true, new MtpRoot[0], null, null));
         mDatabase.getMapper().putDeviceDocument(
-                new MtpDeviceRecord(1, "Device B", true, new MtpRoot[0], new int[0]));
+                new MtpDeviceRecord(1, "Device B", true, new MtpRoot[0], null, null));
         mDatabase.getMapper().stopAddingDocuments(null);
 
         mDatabase.getMapper().startAddingDocuments("1");
@@ -562,7 +562,7 @@
 
         mDatabase.getMapper().startAddingDocuments(null);
         mDatabase.getMapper().putDeviceDocument(
-                new MtpDeviceRecord(0, "Device",  false,  new MtpRoot[0], new int[0]));
+                new MtpDeviceRecord(0, "Device",  false,  new MtpRoot[0], null, null));
         mDatabase.getMapper().stopAddingDocuments(null);
 
         mDatabase.getMapper().startAddingDocuments("1");
@@ -640,7 +640,7 @@
     public void testReplaceExistingRoots() {
         mDatabase.getMapper().startAddingDocuments(null);
         mDatabase.getMapper().putDeviceDocument(
-                new MtpDeviceRecord(0, "Device", true, new MtpRoot[0], new int[0]));
+                new MtpDeviceRecord(0, "Device", true, new MtpRoot[0], null, null));
         mDatabase.getMapper().stopAddingDocuments(null);
 
         // The client code should be able to replace existing rows with new information.
@@ -691,7 +691,7 @@
         // Add one.
         mDatabase.getMapper().startAddingDocuments(null);
         mDatabase.getMapper().putDeviceDocument(
-                new MtpDeviceRecord(0, "Device", true, new MtpRoot[0], new int[0]));
+                new MtpDeviceRecord(0, "Device", true, new MtpRoot[0], null, null));
         mDatabase.getMapper().stopAddingDocuments(null);
 
         mDatabase.getMapper().startAddingDocuments("1");
@@ -745,7 +745,7 @@
         // Add device document.
         mDatabase.getMapper().startAddingDocuments(null);
         mDatabase.getMapper().putDeviceDocument(
-                new MtpDeviceRecord(0, "Device", false, new MtpRoot[0], new int[0]));
+                new MtpDeviceRecord(0, "Device", false, new MtpRoot[0], null, null));
         mDatabase.getMapper().stopAddingDocuments(null);
 
         // It the device does not have storages, it shows a device root.
@@ -895,7 +895,7 @@
     public void testGetDocumentIdForDevice() {
         mDatabase.getMapper().startAddingDocuments(null);
         mDatabase.getMapper().putDeviceDocument(
-                new MtpDeviceRecord(100, "Device", true, new MtpRoot[0], new int[0]));
+                new MtpDeviceRecord(100, "Device", true, new MtpRoot[0], null, null));
         mDatabase.getMapper().stopAddingDocuments(null);
         assertEquals("1", mDatabase.getDocumentIdForDevice(100));
     }
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
index 44841af..b51cf71 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
@@ -69,7 +69,8 @@
                             2048 /* total space */,
                             "" /* no volume identifier */)
                 },
-                new int[0]));
+                null,
+                null));
 
         mProvider.openDevice(0);
         mResolver.waitForNotification(ROOTS_URI, 1);
@@ -109,7 +110,8 @@
                             2048 /* total space */,
                             "" /* no volume identifier */)
                 },
-                new int[0]));
+                null,
+                null));
         mProvider.openDevice(0);
         mResolver.waitForNotification(ROOTS_URI, 1);
     }
@@ -130,7 +132,8 @@
                                 2048 /* total space */,
                                 "" /* no volume identifier */)
                 },
-                new int[0]));
+                null,
+                null));
         mMtpManager.addValidDevice(new MtpDeviceRecord(
                 1,
                 "Device",
@@ -145,7 +148,8 @@
                             4096 /* total space */,
                             "Identifier B" /* no volume identifier */)
                 },
-                new int[0]));
+                null,
+                null));
 
         {
             mProvider.openDevice(0);
@@ -180,7 +184,7 @@
     public void testQueryRoots_error() throws Exception {
         setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
         mMtpManager.addValidDevice(new MtpDeviceRecord(
-                0, "Device A", false /* unopened */, new MtpRoot[0], new int[0]));
+                0, "Device A", false /* unopened */, new MtpRoot[0], null, null));
         mMtpManager.addValidDevice(new MtpDeviceRecord(
                 1,
                 "Device",
@@ -195,7 +199,8 @@
                             4096 /* total space */,
                             "Identifier B" /* no volume identifier */)
                 },
-                new int[0]));
+                null,
+                null));
         {
             mProvider.openDevice(0);
             mProvider.openDevice(1);
@@ -438,7 +443,7 @@
             throws InterruptedException, TimeoutException, IOException {
         final int changeCount = mResolver.getChangeCount(ROOTS_URI);
         mMtpManager.addValidDevice(
-                new MtpDeviceRecord(deviceId, "Device", false /* unopened */, roots, new int[0]));
+                new MtpDeviceRecord(deviceId, "Device", false /* unopened */, roots, null, null));
         mProvider.openDevice(deviceId);
         mResolver.waitForNotification(ROOTS_URI, changeCount + 1);
         return getStrings(mProvider.queryRoots(strings(DocumentsContract.Root.COLUMN_ROOT_ID)));
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java
index 914a4af..9ebe4d1 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java
@@ -87,6 +87,13 @@
         getInstrumentation().show(Arrays.toString(records[0].operationsSupported));
     }
 
+    public void testEventsSupported() {
+        final MtpDeviceRecord[] records = mManager.getDevices();
+        assertEquals(1, records.length);
+        assertNotNull(records[0].eventsSupported);
+        getInstrumentation().show(Arrays.toString(records[0].eventsSupported));
+    }
+
     public void testEventObjectAdded() throws Exception {
         while (true) {
             getInstrumentation().show("Please take a photo by using connected MTP device.");
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
index 3934b88..a1732dc 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
@@ -76,7 +76,7 @@
                 result[i] = device;
             } else {
                 result[i] = new MtpDeviceRecord(
-                        device.deviceId, device.name, device.opened, new MtpRoot[0], new int[0]);
+                        device.deviceId, device.name, device.opened, new MtpRoot[0], null, null);
             }
         }
         return result;
@@ -90,7 +90,7 @@
         }
         mDevices.put(
                 deviceId,
-                new MtpDeviceRecord(device.deviceId, device.name, true, device.roots, new int[0]));
+                new MtpDeviceRecord(device.deviceId, device.name, true, device.roots, null, null));
     }
 
     @Override
@@ -101,7 +101,7 @@
         }
         mDevices.put(
                 deviceId,
-                new MtpDeviceRecord(device.deviceId, device.name, false, device.roots, new int[0]));
+                new MtpDeviceRecord(device.deviceId, device.name, false, device.roots, null, null));
     }
 
     @Override