1d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood/* 2d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood * Copyright (C) 2010 The Android Open Source Project 3d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood * 4d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood * Licensed under the Apache License, Version 2.0 (the "License"); 5d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood * you may not use this file except in compliance with the License. 6d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood * You may obtain a copy of the License at 7d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood * 8d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood * http://www.apache.org/licenses/LICENSE-2.0 9d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood * 10d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood * Unless required by applicable law or agreed to in writing, software 11d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood * distributed under the License is distributed on an "AS IS" BASIS, 12d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood * See the License for the specific language governing permissions and 14d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood * limitations under the License. 15d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood */ 16d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood 170cd0136d440cf6ad9d5fab430269116786e671ecMike Lockwoodpackage android.mtp; 18d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood 1956c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwoodimport android.content.BroadcastReceiver; 2060cfad80bdf61db436643927337c2fb30186e99dJeff Sharkeyimport android.content.ContentProviderClient; 21d815f79766984fce499e147ecbacc01914683f74Mike Lockwoodimport android.content.ContentValues; 2260cfad80bdf61db436643927337c2fb30186e99dJeff Sharkeyimport android.content.Context; 232837eefc5459427138c080d445bb491c75630163Mike Lockwoodimport android.content.Intent; 2456c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwoodimport android.content.IntentFilter; 25775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwoodimport android.content.SharedPreferences; 26d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwoodimport android.database.Cursor; 2759e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwoodimport android.database.sqlite.SQLiteDatabase; 280cd0136d440cf6ad9d5fab430269116786e671ecMike Lockwoodimport android.media.MediaScanner; 29d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwoodimport android.net.Uri; 3056c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwoodimport android.os.BatteryManager; 31d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwoodimport android.os.RemoteException; 3213bb2f4ddadcfee8e82a0eea3cb76a2448bb2ccbJerry Zhangimport android.os.SystemProperties; 33f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhangimport android.os.storage.StorageVolume; 34a31560598af25d5116effc2a6af0d9be12a0628dMike Lockwoodimport android.provider.MediaStore; 359a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwoodimport android.provider.MediaStore.Audio; 363b2a62e5b35ea5f8da9d9bee52f75c9c2ebb4eceMike Lockwoodimport android.provider.MediaStore.Files; 37ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwoodimport android.provider.MediaStore.MediaColumns; 38d470a1eca6ec47fe09513e40788e0abea3d12c1fJerry Zhangimport android.system.ErrnoException; 39d470a1eca6ec47fe09513e40788e0abea3d12c1fJerry Zhangimport android.system.Os; 40d470a1eca6ec47fe09513e40788e0abea3d12c1fJerry Zhangimport android.system.OsConstants; 41d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwoodimport android.util.Log; 42ea93fa16b166ff477e9cf6bd3ac67c8fb259fb57Mike Lockwoodimport android.view.Display; 43ea93fa16b166ff477e9cf6bd3ac67c8fb259fb57Mike Lockwoodimport android.view.WindowManager; 44d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood 4560cfad80bdf61db436643927337c2fb30186e99dJeff Sharkeyimport dalvik.system.CloseGuard; 4660cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey 47f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhangimport com.google.android.collect.Sets; 48f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang 495ebac83e20dadf10bd4a0cee4ddf69d0ec049f98Mike Lockwoodimport java.io.File; 50f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhangimport java.nio.file.Path; 51f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhangimport java.nio.file.Paths; 52f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhangimport java.util.ArrayList; 53f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhangimport java.util.Arrays; 547d7fb63071ca370f77ed69055ac30341b8388d15Mike Lockwoodimport java.util.HashMap; 55f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhangimport java.util.Iterator; 56fe464a76d8fc858be971dbb5e67b1d22fd925347dujin.chaimport java.util.Locale; 5760cfad80bdf61db436643927337c2fb30186e99dJeff Sharkeyimport java.util.concurrent.atomic.AtomicBoolean; 58f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhangimport java.util.stream.IntStream; 59f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhangimport java.util.stream.Stream; 605ebac83e20dadf10bd4a0cee4ddf69d0ec049f98Mike Lockwood 61d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood/** 62f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang * MtpDatabase provides an interface for MTP operations that MtpServer can use. To do this, it uses 63f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang * MtpStorageManager for filesystem operations and MediaProvider to get media metadata. File 64f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang * operations are also reflected in MediaProvider if possible. 65f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang * operations 66d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood * {@hide} 67d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood */ 6860cfad80bdf61db436643927337c2fb30186e99dJeff Sharkeypublic class MtpDatabase implements AutoCloseable { 69f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang private static final String TAG = MtpDatabase.class.getSimpleName(); 70d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood 712837eefc5459427138c080d445bb491c75630163Mike Lockwood private final Context mContext; 7260cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey private final ContentProviderClient mMediaProvider; 73d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood private final String mVolumeName; 74d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood private final Uri mObjectsUri; 7560cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey private final MediaScanner mMediaScanner; 7660cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey 7760cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey private final AtomicBoolean mClosed = new AtomicBoolean(); 7860cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey private final CloseGuard mCloseGuard = CloseGuard.get(); 7960cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey 80f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang private final HashMap<String, MtpStorage> mStorageMap = new HashMap<>(); 81d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood 827d7fb63071ca370f77ed69055ac30341b8388d15Mike Lockwood // cached property groups for single properties 83f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang private final HashMap<Integer, MtpPropertyGroup> mPropertyGroupsByProperty = new HashMap<>(); 847d7fb63071ca370f77ed69055ac30341b8388d15Mike Lockwood 857d7fb63071ca370f77ed69055ac30341b8388d15Mike Lockwood // cached property groups for all properties for a given format 86f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang private final HashMap<Integer, MtpPropertyGroup> mPropertyGroupsByFormat = new HashMap<>(); 872837eefc5459427138c080d445bb491c75630163Mike Lockwood 88775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood // SharedPreferences for writable MTP device properties 89775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood private SharedPreferences mDeviceProperties; 9059e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood 91f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang // Cached device properties 92f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang private int mBatteryLevel; 93f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang private int mBatteryScale; 94f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang private int mDeviceType; 95f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang 96f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang private MtpServer mServer; 97f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang private MtpStorageManager mManager; 98f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang 99f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang private static final String PATH_WHERE = Files.FileColumns.DATA + "=?"; 100f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang private static final String[] ID_PROJECTION = new String[] {Files.FileColumns._ID}; 101f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang private static final String[] PATH_PROJECTION = new String[] {Files.FileColumns.DATA}; 102f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang private static final String NO_MEDIA = ".nomedia"; 103f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang 104f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang static { 105f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang System.loadLibrary("media_jni"); 106f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } 107f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang 108f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang private static final int[] PLAYBACK_FORMATS = { 109f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang // allow transferring arbitrary files 110f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.FORMAT_UNDEFINED, 111f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang 112f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.FORMAT_ASSOCIATION, 113f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.FORMAT_TEXT, 114f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.FORMAT_HTML, 115f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.FORMAT_WAV, 116f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.FORMAT_MP3, 117f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.FORMAT_MPEG, 118f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.FORMAT_EXIF_JPEG, 119f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.FORMAT_TIFF_EP, 120f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.FORMAT_BMP, 121f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.FORMAT_GIF, 122f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.FORMAT_JFIF, 123f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.FORMAT_PNG, 124f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.FORMAT_TIFF, 125f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.FORMAT_WMA, 126f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.FORMAT_OGG, 127f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.FORMAT_AAC, 128f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.FORMAT_MP4_CONTAINER, 129f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.FORMAT_MP2, 130f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.FORMAT_3GP_CONTAINER, 131f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.FORMAT_ABSTRACT_AV_PLAYLIST, 132f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.FORMAT_WPL_PLAYLIST, 133f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.FORMAT_M3U_PLAYLIST, 134f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.FORMAT_PLS_PLAYLIST, 135f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.FORMAT_XML_DOCUMENT, 136f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.FORMAT_FLAC, 137f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.FORMAT_DNG, 138f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.FORMAT_HEIF, 139d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood }; 140f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang 141f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang private static final int[] FILE_PROPERTIES = { 142f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.PROPERTY_STORAGE_ID, 143f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.PROPERTY_OBJECT_FORMAT, 144f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.PROPERTY_PROTECTION_STATUS, 145f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.PROPERTY_OBJECT_SIZE, 146f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.PROPERTY_OBJECT_FILE_NAME, 147f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.PROPERTY_DATE_MODIFIED, 148f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.PROPERTY_PERSISTENT_UID, 149f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.PROPERTY_PARENT_OBJECT, 150f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.PROPERTY_NAME, 151f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.PROPERTY_DISPLAY_NAME, 152f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.PROPERTY_DATE_ADDED, 1535ebac83e20dadf10bd4a0cee4ddf69d0ec049f98Mike Lockwood }; 154f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang 155f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang private static final int[] AUDIO_PROPERTIES = { 156f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.PROPERTY_ARTIST, 157f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.PROPERTY_ALBUM_NAME, 158f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.PROPERTY_ALBUM_ARTIST, 159f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.PROPERTY_TRACK, 160f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.PROPERTY_ORIGINAL_RELEASE_DATE, 161f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.PROPERTY_DURATION, 162f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.PROPERTY_GENRE, 163f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.PROPERTY_COMPOSER, 164f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.PROPERTY_AUDIO_WAVE_CODEC, 165f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.PROPERTY_BITRATE_TYPE, 166f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.PROPERTY_AUDIO_BITRATE, 167f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.PROPERTY_NUMBER_OF_CHANNELS, 168f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.PROPERTY_SAMPLE_RATE, 16971827748105252791854b87da8e0e0c66ff0d2cbMike Lockwood }; 170f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang 171f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang private static final int[] VIDEO_PROPERTIES = { 172f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.PROPERTY_ARTIST, 173f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.PROPERTY_ALBUM_NAME, 174f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.PROPERTY_DURATION, 175f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.PROPERTY_DESCRIPTION, 176d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood }; 177f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang 178f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang private static final int[] IMAGE_PROPERTIES = { 179f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.PROPERTY_DESCRIPTION, 180d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood }; 1816acc90fc216530f0a5bbf7339066af9b415cbb02Mike Lockwood 182f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang private static final int[] DEVICE_PROPERTIES = { 183f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER, 184f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME, 185f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.DEVICE_PROPERTY_IMAGE_SIZE, 186f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.DEVICE_PROPERTY_BATTERY_LEVEL, 187f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpConstants.DEVICE_PROPERTY_PERCEIVED_DEVICE_TYPE, 188f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang }; 189d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood 190f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang private int[] getSupportedObjectProperties(int format) { 191f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang switch (format) { 192f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang case MtpConstants.FORMAT_MP3: 193f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang case MtpConstants.FORMAT_WAV: 194f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang case MtpConstants.FORMAT_WMA: 195f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang case MtpConstants.FORMAT_OGG: 196f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang case MtpConstants.FORMAT_AAC: 197f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return IntStream.concat(Arrays.stream(FILE_PROPERTIES), 198f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang Arrays.stream(AUDIO_PROPERTIES)).toArray(); 199f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang case MtpConstants.FORMAT_MPEG: 200f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang case MtpConstants.FORMAT_3GP_CONTAINER: 201f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang case MtpConstants.FORMAT_WMV: 202f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return IntStream.concat(Arrays.stream(FILE_PROPERTIES), 203f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang Arrays.stream(VIDEO_PROPERTIES)).toArray(); 204f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang case MtpConstants.FORMAT_EXIF_JPEG: 205f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang case MtpConstants.FORMAT_GIF: 206f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang case MtpConstants.FORMAT_PNG: 207f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang case MtpConstants.FORMAT_BMP: 208f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang case MtpConstants.FORMAT_DNG: 209f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang case MtpConstants.FORMAT_HEIF: 210f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return IntStream.concat(Arrays.stream(FILE_PROPERTIES), 211f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang Arrays.stream(IMAGE_PROPERTIES)).toArray(); 212f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang default: 213f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return FILE_PROPERTIES; 214f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } 215f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } 21656c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood 217f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang private int[] getSupportedDeviceProperties() { 218f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return DEVICE_PROPERTIES; 219f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } 220d815f79766984fce499e147ecbacc01914683f74Mike Lockwood 221f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang private int[] getSupportedPlaybackFormats() { 222f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return PLAYBACK_FORMATS; 223f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } 22413bb2f4ddadcfee8e82a0eea3cb76a2448bb2ccbJerry Zhang 225f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang private int[] getSupportedCaptureFormats() { 226f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang // no capture formats yet 227f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return null; 228d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood } 229d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood 23056c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood private BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() { 231f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang @Override 23256c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood public void onReceive(Context context, Intent intent) { 23356c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood String action = intent.getAction(); 23456c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood if (action.equals(Intent.ACTION_BATTERY_CHANGED)) { 23556c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood mBatteryScale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 0); 23656c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood int newLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0); 23756c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood if (newLevel != mBatteryLevel) { 23856c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood mBatteryLevel = newLevel; 23956c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood if (mServer != null) { 24056c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood // send device property changed event 24156c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood mServer.sendDevicePropertyChanged( 24256c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood MtpConstants.DEVICE_PROPERTY_BATTERY_LEVEL); 24356c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood } 24456c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood } 24556c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood } 24656c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood } 24756c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood }; 24856c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood 24963a69fd4f8d21c19fefc88437cc27a96775a87caJerry Zhang public MtpDatabase(Context context, String volumeName, 25073e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood String[] subDirectories) { 251d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood native_setup(); 2522837eefc5459427138c080d445bb491c75630163Mike Lockwood mContext = context; 25363a69fd4f8d21c19fefc88437cc27a96775a87caJerry Zhang mMediaProvider = context.getContentResolver() 25460cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey .acquireContentProviderClient(MediaStore.AUTHORITY); 255d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood mVolumeName = volumeName; 2568490e66f57506d4e4b05e7c987c7ca34295843e6Mike Lockwood mObjectsUri = Files.getMtpObjectsUri(volumeName); 25760cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey mMediaScanner = new MediaScanner(context, mVolumeName); 258f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang mManager = new MtpStorageManager(new MtpStorageManager.MtpNotifier() { 259f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang @Override 260f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang public void sendObjectAdded(int id) { 261f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (MtpDatabase.this.mServer != null) 262f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpDatabase.this.mServer.sendObjectAdded(id); 26373e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood } 264f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang 265f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang @Override 266f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang public void sendObjectRemoved(int id) { 267f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (MtpDatabase.this.mServer != null) 268f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpDatabase.this.mServer.sendObjectRemoved(id); 26973e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood } 270f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang }, subDirectories == null ? null : Sets.newHashSet(subDirectories)); 27173e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood 272775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood initDeviceProperties(context); 27313bb2f4ddadcfee8e82a0eea3cb76a2448bb2ccbJerry Zhang mDeviceType = SystemProperties.getInt("sys.usb.mtp.device_type", 0); 27460cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey mCloseGuard.open("close"); 275d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood } 276d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood 27756c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood public void setServer(MtpServer server) { 27856c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood mServer = server; 2791632fae376eb460cf992e543b13c8690b71fe8d5Marco Nelissen // always unregister before registering 2801632fae376eb460cf992e543b13c8690b71fe8d5Marco Nelissen try { 2811632fae376eb460cf992e543b13c8690b71fe8d5Marco Nelissen mContext.unregisterReceiver(mBatteryReceiver); 2821632fae376eb460cf992e543b13c8690b71fe8d5Marco Nelissen } catch (IllegalArgumentException e) { 2831632fae376eb460cf992e543b13c8690b71fe8d5Marco Nelissen // wasn't previously registered, ignore 2841632fae376eb460cf992e543b13c8690b71fe8d5Marco Nelissen } 28556c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood // register for battery notifications when we are connected 28656c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood if (server != null) { 28756c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood mContext.registerReceiver(mBatteryReceiver, 28856c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); 28956c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood } 29056c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood } 29156c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood 292d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood @Override 29360cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey public void close() { 294f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang mManager.close(); 29560cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey mCloseGuard.close(); 29660cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey if (mClosed.compareAndSet(false, true)) { 29760cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey mMediaScanner.close(); 298484ea675dd5480db6e272c0fcb46ec5a9d733a24Jerry Zhang if (mMediaProvider != null) { 299484ea675dd5480db6e272c0fcb46ec5a9d733a24Jerry Zhang mMediaProvider.close(); 300484ea675dd5480db6e272c0fcb46ec5a9d733a24Jerry Zhang } 30160cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey native_finalize(); 30260cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey } 30360cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey } 30460cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey 30560cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey @Override 306dbead321daf2c16dc74e182bec591b278cfccb2cMike Lockwood protected void finalize() throws Throwable { 307dbead321daf2c16dc74e182bec591b278cfccb2cMike Lockwood try { 308492e9e851cadca62df84eaff1a3c1ba788492fbaNarayan Kamath if (mCloseGuard != null) { 309492e9e851cadca62df84eaff1a3c1ba788492fbaNarayan Kamath mCloseGuard.warnIfOpen(); 310492e9e851cadca62df84eaff1a3c1ba788492fbaNarayan Kamath } 31160cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey close(); 312dbead321daf2c16dc74e182bec591b278cfccb2cMike Lockwood } finally { 313dbead321daf2c16dc74e182bec591b278cfccb2cMike Lockwood super.finalize(); 314dbead321daf2c16dc74e182bec591b278cfccb2cMike Lockwood } 315d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood } 316d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood 317f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang public void addStorage(StorageVolume storage) { 318f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpStorage mtpStorage = mManager.addMtpStorage(storage); 319f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang mStorageMap.put(storage.getPath(), mtpStorage); 3202ecbc7acdb6f87e34228e67e36333f64eb021f82Jerry Zhang if (mServer != null) { 3212ecbc7acdb6f87e34228e67e36333f64eb021f82Jerry Zhang mServer.addStorage(mtpStorage); 3222ecbc7acdb6f87e34228e67e36333f64eb021f82Jerry Zhang } 323b239b683765f63d79e74b8ba2a8cc2855f236536Mike Lockwood } 324b239b683765f63d79e74b8ba2a8cc2855f236536Mike Lockwood 325f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang public void removeStorage(StorageVolume storage) { 326f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpStorage mtpStorage = mStorageMap.get(storage.getPath()); 327f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (mtpStorage == null) { 328f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return; 329f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } 3302ecbc7acdb6f87e34228e67e36333f64eb021f82Jerry Zhang if (mServer != null) { 3312ecbc7acdb6f87e34228e67e36333f64eb021f82Jerry Zhang mServer.removeStorage(mtpStorage); 3322ecbc7acdb6f87e34228e67e36333f64eb021f82Jerry Zhang } 333f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang mManager.removeMtpStorage(mtpStorage); 334b239b683765f63d79e74b8ba2a8cc2855f236536Mike Lockwood mStorageMap.remove(storage.getPath()); 335b239b683765f63d79e74b8ba2a8cc2855f236536Mike Lockwood } 336b239b683765f63d79e74b8ba2a8cc2855f236536Mike Lockwood 337775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood private void initDeviceProperties(Context context) { 338775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood final String devicePropertiesName = "device-properties"; 339f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang mDeviceProperties = context.getSharedPreferences(devicePropertiesName, 340f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang Context.MODE_PRIVATE); 341775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood File databaseFile = context.getDatabasePath(devicePropertiesName); 342775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood 343775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood if (databaseFile.exists()) { 344775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood // for backward compatibility - read device properties from sqlite database 345775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood // and migrate them to shared prefs 346775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood SQLiteDatabase db = null; 347775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood Cursor c = null; 348775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood try { 349775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood db = context.openOrCreateDatabase("device-properties", Context.MODE_PRIVATE, null); 350775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood if (db != null) { 351f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang c = db.query("properties", new String[]{"_id", "code", "value"}, 352775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood null, null, null, null, null); 353775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood if (c != null) { 354775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood SharedPreferences.Editor e = mDeviceProperties.edit(); 355775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood while (c.moveToNext()) { 356775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood String name = c.getString(1); 357775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood String value = c.getString(2); 358775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood e.putString(name, value); 359775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood } 360775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood e.commit(); 361775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood } 362775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood } 363775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood } catch (Exception e) { 364775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood Log.e(TAG, "failed to migrate device properties", e); 365775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood } finally { 366775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood if (c != null) c.close(); 367775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood if (db != null) db.close(); 368775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood } 3693ed02535fdc3c752346c40b57962905b7072e693jangwon.lee context.deleteDatabase(devicePropertiesName); 37059e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood } 37159e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood } 37259e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood 373f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang private int beginSendObject(String path, int format, int parent, int storageId) { 374f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpStorageManager.MtpObject parentObj = 375f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang parent == 0 ? mManager.getStorageRoot(storageId) : mManager.getObject(parent); 376f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (parentObj == null) { 377f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return -1; 37873e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood } 37973e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood 380f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang Path objPath = Paths.get(path); 381f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return mManager.beginSendObject(parentObj, objPath.getFileName().toString(), format); 3825f411696a62b7f0fb95ba3519aa127c09f155eedMarco Nelissen } 3835f411696a62b7f0fb95ba3519aa127c09f155eedMarco Nelissen 384f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang private void endSendObject(int handle, boolean succeeded) { 385f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpStorageManager.MtpObject obj = mManager.getObject(handle); 386f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (obj == null || !mManager.endSendObject(obj, succeeded)) { 387f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang Log.e(TAG, "Failed to successfully end send object"); 388f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return; 3895f411696a62b7f0fb95ba3519aa127c09f155eedMarco Nelissen } 390f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang // Add the new file to MediaProvider 391f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (succeeded) { 392f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang String path = obj.getPath().toString(); 393f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang int format = obj.getFormat(); 394f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang // Get parent info from MediaProvider, since the id is different from MTP's 395f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang ContentValues values = new ContentValues(); 396f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang values.put(Files.FileColumns.DATA, path); 397f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang values.put(Files.FileColumns.FORMAT, format); 398f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang values.put(Files.FileColumns.SIZE, obj.getSize()); 399f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang values.put(Files.FileColumns.DATE_MODIFIED, obj.getModifiedTime()); 400bafca214eaf326441e5157d645b2ee7fe8c7d1abMike Lockwood try { 401f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (obj.getParent().isRoot()) { 402f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang values.put(Files.FileColumns.PARENT, 0); 403f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } else { 404f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang int parentId = findInMedia(obj.getParent().getPath()); 405f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (parentId != -1) { 406f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang values.put(Files.FileColumns.PARENT, parentId); 407f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } else { 408f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang // The parent isn't in MediaProvider. Don't add the new file. 409f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return; 410f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } 411f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } 412f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang 413f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang Uri uri = mMediaProvider.insert(mObjectsUri, values); 414f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (uri != null) { 415f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang rescanFile(path, Integer.parseInt(uri.getPathSegments().get(2)), format); 416bafca214eaf326441e5157d645b2ee7fe8c7d1abMike Lockwood } 417bafca214eaf326441e5157d645b2ee7fe8c7d1abMike Lockwood } catch (RemoteException e) { 418bafca214eaf326441e5157d645b2ee7fe8c7d1abMike Lockwood Log.e(TAG, "RemoteException in beginSendObject", e); 419d815f79766984fce499e147ecbacc01914683f74Mike Lockwood } 420d815f79766984fce499e147ecbacc01914683f74Mike Lockwood } 421d815f79766984fce499e147ecbacc01914683f74Mike Lockwood } 422d815f79766984fce499e147ecbacc01914683f74Mike Lockwood 423f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang private void rescanFile(String path, int handle, int format) { 424f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang // handle abstract playlists separately 425f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang // they do not exist in the file system so don't use the media scanner here 426f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (format == MtpConstants.FORMAT_ABSTRACT_AV_PLAYLIST) { 427f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang // extract name from path 428f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang String name = path; 429f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang int lastSlash = name.lastIndexOf('/'); 430f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (lastSlash >= 0) { 431f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang name = name.substring(lastSlash + 1); 432f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } 433f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang // strip trailing ".pla" from the name 434f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (name.endsWith(".pla")) { 435f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang name = name.substring(0, name.length() - 4); 436f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } 4379a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood 438f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang ContentValues values = new ContentValues(1); 439f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang values.put(Audio.Playlists.DATA, path); 440f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang values.put(Audio.Playlists.NAME, name); 441f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang values.put(Files.FileColumns.FORMAT, format); 442f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang values.put(Files.FileColumns.DATE_MODIFIED, System.currentTimeMillis() / 1000); 443f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang values.put(MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID, handle); 444f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang try { 445f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang mMediaProvider.insert( 446f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang Audio.Playlists.EXTERNAL_CONTENT_URI, values); 447f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } catch (RemoteException e) { 448f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang Log.e(TAG, "RemoteException in endSendObject", e); 4499a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood } 450d815f79766984fce499e147ecbacc01914683f74Mike Lockwood } else { 451f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang mMediaScanner.scanMtpFile(path, handle, format); 452d815f79766984fce499e147ecbacc01914683f74Mike Lockwood } 453d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood } 454d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood 455f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang private int[] getObjectList(int storageID, int format, int parent) { 456f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang Stream<MtpStorageManager.MtpObject> objectStream = mManager.getObjects(parent, 457f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang format, storageID); 458f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (objectStream == null) { 459f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return null; 460f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } 461f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return objectStream.mapToInt(MtpStorageManager.MtpObject::getId).toArray(); 462b4aa69fd116d5486f224914350a57b4befddfe26kyle_tso } 463b4aa69fd116d5486f224914350a57b4befddfe26kyle_tso 464f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang private int getNumObjects(int storageID, int format, int parent) { 465f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang Stream<MtpStorageManager.MtpObject> objectStream = mManager.getObjects(parent, 466f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang format, storageID); 467f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (objectStream == null) { 468f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return -1; 46973e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood } 470f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return (int) objectStream.count(); 471f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } 47273e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood 473f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang private MtpPropertyList getObjectPropertyList(int handle, int format, int property, 474f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang int groupCode, int depth) { 475f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang // FIXME - implement group support 476f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (property == 0) { 477f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (groupCode == 0) { 478f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return new MtpPropertyList(MtpConstants.RESPONSE_PARAMETER_NOT_SUPPORTED); 479d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood } 480f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return new MtpPropertyList(MtpConstants.RESPONSE_SPECIFICATION_BY_GROUP_UNSUPPORTED); 481b239b683765f63d79e74b8ba2a8cc2855f236536Mike Lockwood } 482f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (depth == 0xFFFFFFFF && (handle == 0 || handle == 0xFFFFFFFF)) { 483f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang // request all objects starting at root 484f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang handle = 0xFFFFFFFF; 485f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang depth = 0; 486f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } 487f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (!(depth == 0 || depth == 1)) { 488f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang // we only support depth 0 and 1 489f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang // depth 0: single object, depth 1: immediate children 490f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return new MtpPropertyList(MtpConstants.RESPONSE_SPECIFICATION_BY_DEPTH_UNSUPPORTED); 491f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } 492f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang Stream<MtpStorageManager.MtpObject> objectStream = Stream.of(); 493f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (handle == 0xFFFFFFFF) { 494f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang // All objects are requested 495f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang objectStream = mManager.getObjects(0, format, 0xFFFFFFFF); 496f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (objectStream == null) { 497f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return new MtpPropertyList(MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE); 498d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood } 499f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } else if (handle != 0) { 500f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang // Add the requested object if format matches 501f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpStorageManager.MtpObject obj = mManager.getObject(handle); 502f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (obj == null) { 503f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return new MtpPropertyList(MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE); 504d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood } 505f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (obj.getFormat() == format || format == 0) { 506f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang objectStream = Stream.of(obj); 507d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood } 508d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood } 509f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (handle == 0 || depth == 1) { 510f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (handle == 0) { 511f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang handle = 0xFFFFFFFF; 5127a047c89862febf63ce1bb5873179c7a2ff8d9d3Mike Lockwood } 513f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang // Get the direct children of root or this object. 514f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang Stream<MtpStorageManager.MtpObject> childStream = mManager.getObjects(handle, format, 515f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang 0xFFFFFFFF); 516f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (childStream == null) { 517f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return new MtpPropertyList(MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE); 5187a047c89862febf63ce1bb5873179c7a2ff8d9d3Mike Lockwood } 519f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang objectStream = Stream.concat(objectStream, childStream); 5205ebac83e20dadf10bd4a0cee4ddf69d0ec049f98Mike Lockwood } 5215ebac83e20dadf10bd4a0cee4ddf69d0ec049f98Mike Lockwood 522f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpPropertyList ret = new MtpPropertyList(MtpConstants.RESPONSE_OK); 5237d7fb63071ca370f77ed69055ac30341b8388d15Mike Lockwood MtpPropertyGroup propertyGroup; 524f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang Iterator<MtpStorageManager.MtpObject> iter = objectStream.iterator(); 525f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang while (iter.hasNext()) { 526f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpStorageManager.MtpObject obj = iter.next(); 527f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (property == 0xffffffff) { 528f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang // Get all properties supported by this object 529f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang propertyGroup = mPropertyGroupsByFormat.get(obj.getFormat()); 530f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (propertyGroup == null) { 531f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang int[] propertyList = getSupportedObjectProperties(format); 532f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang propertyGroup = new MtpPropertyGroup(mMediaProvider, mVolumeName, 533f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang propertyList); 534f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang mPropertyGroupsByFormat.put(format, propertyGroup); 535f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } 536f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } else { 537f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang // Get this property value 538f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang final int[] propertyList = new int[]{property}; 539f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang propertyGroup = mPropertyGroupsByProperty.get(property); 540f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (propertyGroup == null) { 541f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang propertyGroup = new MtpPropertyGroup(mMediaProvider, mVolumeName, 542f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang propertyList); 543f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang mPropertyGroupsByProperty.put(property, propertyGroup); 544f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } 545e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood } 546f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang int err = propertyGroup.getPropertyList(obj, ret); 547f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (err != MtpConstants.RESPONSE_OK) { 548f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return new MtpPropertyList(err); 549e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood } 550e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood } 551f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return ret; 552e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood } 553e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood 554e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood private int renameFile(int handle, String newName) { 555f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpStorageManager.MtpObject obj = mManager.getObject(handle); 556f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (obj == null) { 557e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE; 558e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood } 559f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang Path oldPath = obj.getPath(); 56073e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood 561e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood // now rename the file. make sure this succeeds before updating database 562f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (!mManager.beginRenameObject(obj, newName)) 563e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood return MtpConstants.RESPONSE_GENERAL_ERROR; 564f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang Path newPath = obj.getPath(); 565f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang boolean success = oldPath.toFile().renameTo(newPath.toFile()); 566d470a1eca6ec47fe09513e40788e0abea3d12c1fJerry Zhang try { 567d470a1eca6ec47fe09513e40788e0abea3d12c1fJerry Zhang Os.access(oldPath.toString(), OsConstants.F_OK); 568d470a1eca6ec47fe09513e40788e0abea3d12c1fJerry Zhang Os.access(newPath.toString(), OsConstants.F_OK); 569d470a1eca6ec47fe09513e40788e0abea3d12c1fJerry Zhang } catch (ErrnoException e) { 570d470a1eca6ec47fe09513e40788e0abea3d12c1fJerry Zhang // Ignore. Could fail if the metadata was already updated. 571d470a1eca6ec47fe09513e40788e0abea3d12c1fJerry Zhang } 572d470a1eca6ec47fe09513e40788e0abea3d12c1fJerry Zhang 573f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (!mManager.endRenameObject(obj, oldPath.getFileName().toString(), success)) { 574f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang Log.e(TAG, "Failed to end rename object"); 575e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood } 576e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood if (!success) { 577e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood return MtpConstants.RESPONSE_GENERAL_ERROR; 578e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood } 579e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood 580f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang // finally update MediaProvider 581e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood ContentValues values = new ContentValues(); 582f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang values.put(Files.FileColumns.DATA, newPath.toString()); 583f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang String[] whereArgs = new String[]{oldPath.toString()}; 584e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood try { 585e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood // note - we are relying on a special case in MediaProvider.update() to update 586e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood // the paths for all children in the case where this is a directory. 587f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang mMediaProvider.update(mObjectsUri, values, PATH_WHERE, whereArgs); 588e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood } catch (RemoteException e) { 589e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood Log.e(TAG, "RemoteException in mMediaProvider.update", e); 590e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood } 591e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood 592ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen // check if nomedia status changed 593f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (obj.isDir()) { 594ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen // for directories, check if renamed from something hidden to something non-hidden 595f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (oldPath.getFileName().startsWith(".") && !newPath.startsWith(".")) { 596ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen // directory was unhidden 597ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen try { 598f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang mMediaProvider.call(MediaStore.UNHIDE_CALL, newPath.toString(), null); 599ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen } catch (RemoteException e) { 600ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen Log.e(TAG, "failed to unhide/rescan for " + newPath); 601ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen } 602ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen } 603ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen } else { 604ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen // for files, check if renamed from .nomedia to something else 605f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (oldPath.getFileName().toString().toLowerCase(Locale.US).equals(NO_MEDIA) 606f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang && !newPath.getFileName().toString().toLowerCase(Locale.US).equals(NO_MEDIA)) { 607ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen try { 608f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang mMediaProvider.call(MediaStore.UNHIDE_CALL, 609f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang oldPath.getParent().toString(), null); 610ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen } catch (RemoteException e) { 611ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen Log.e(TAG, "failed to unhide/rescan for " + newPath); 612ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen } 613ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen } 614ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen } 615e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood return MtpConstants.RESPONSE_OK; 616d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood } 617d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood 618f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang private int beginMoveObject(int handle, int newParent, int newStorage) { 619f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpStorageManager.MtpObject obj = mManager.getObject(handle); 620f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpStorageManager.MtpObject parent = newParent == 0 ? 621f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang mManager.getStorageRoot(newStorage) : mManager.getObject(newParent); 622f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (obj == null || parent == null) 623f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE; 624f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang 625f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang boolean allowed = mManager.beginMoveObject(obj, parent); 626f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return allowed ? MtpConstants.RESPONSE_OK : MtpConstants.RESPONSE_GENERAL_ERROR; 627f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } 628952558d4e5a51b5f69c7c0bc1e3d7fa8da1cbf3cJerry Zhang 629f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang private void endMoveObject(int oldParent, int newParent, int oldStorage, int newStorage, 630f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang int objId, boolean success) { 631f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpStorageManager.MtpObject oldParentObj = oldParent == 0 ? 632f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang mManager.getStorageRoot(oldStorage) : mManager.getObject(oldParent); 633f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpStorageManager.MtpObject newParentObj = newParent == 0 ? 634f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang mManager.getStorageRoot(newStorage) : mManager.getObject(newParent); 635f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpStorageManager.MtpObject obj = mManager.getObject(objId); 636f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang String name = obj.getName(); 637f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (newParentObj == null || oldParentObj == null 638f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang ||!mManager.endMoveObject(oldParentObj, newParentObj, name, success)) { 639f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang Log.e(TAG, "Failed to end move object"); 640f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return; 641952558d4e5a51b5f69c7c0bc1e3d7fa8da1cbf3cJerry Zhang } 642952558d4e5a51b5f69c7c0bc1e3d7fa8da1cbf3cJerry Zhang 643f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang obj = mManager.getObject(objId); 644f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (!success || obj == null) 645f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return; 646f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang // Get parent info from MediaProvider, since the id is different from MTP's 647952558d4e5a51b5f69c7c0bc1e3d7fa8da1cbf3cJerry Zhang ContentValues values = new ContentValues(); 648f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang Path path = newParentObj.getPath().resolve(name); 649f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang Path oldPath = oldParentObj.getPath().resolve(name); 650f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang values.put(Files.FileColumns.DATA, path.toString()); 651f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (obj.getParent().isRoot()) { 652f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang values.put(Files.FileColumns.PARENT, 0); 653f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } else { 654f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang int parentId = findInMedia(path.getParent()); 655f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (parentId != -1) { 656f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang values.put(Files.FileColumns.PARENT, parentId); 657f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } else { 658f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang // The new parent isn't in MediaProvider, so delete the object instead 659f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang deleteFromMedia(oldPath, obj.isDir()); 660f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return; 661f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } 662f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } 663f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang // update MediaProvider 664f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang Cursor c = null; 665f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang String[] whereArgs = new String[]{oldPath.toString()}; 666952558d4e5a51b5f69c7c0bc1e3d7fa8da1cbf3cJerry Zhang try { 667f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang int parentId = -1; 668f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (!oldParentObj.isRoot()) { 669f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang parentId = findInMedia(oldPath.getParent()); 670f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } 671f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (oldParentObj.isRoot() || parentId != -1) { 672f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang // Old parent exists in MediaProvider - perform a move 673f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang // note - we are relying on a special case in MediaProvider.update() to update 674f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang // the paths for all children in the case where this is a directory. 675f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang mMediaProvider.update(mObjectsUri, values, PATH_WHERE, whereArgs); 676f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } else { 677f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang // Old parent doesn't exist - add the object 678f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang values.put(Files.FileColumns.FORMAT, obj.getFormat()); 679f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang values.put(Files.FileColumns.SIZE, obj.getSize()); 680f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang values.put(Files.FileColumns.DATE_MODIFIED, obj.getModifiedTime()); 681f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang Uri uri = mMediaProvider.insert(mObjectsUri, values); 682f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (uri != null) { 683f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang rescanFile(path.toString(), 684f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang Integer.parseInt(uri.getPathSegments().get(2)), obj.getFormat()); 685f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } 686f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } 687952558d4e5a51b5f69c7c0bc1e3d7fa8da1cbf3cJerry Zhang } catch (RemoteException e) { 688952558d4e5a51b5f69c7c0bc1e3d7fa8da1cbf3cJerry Zhang Log.e(TAG, "RemoteException in mMediaProvider.update", e); 689952558d4e5a51b5f69c7c0bc1e3d7fa8da1cbf3cJerry Zhang } 690f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } 691f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang 692f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang private int beginCopyObject(int handle, int newParent, int newStorage) { 693f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpStorageManager.MtpObject obj = mManager.getObject(handle); 694f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpStorageManager.MtpObject parent = newParent == 0 ? 695f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang mManager.getStorageRoot(newStorage) : mManager.getObject(newParent); 696f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (obj == null || parent == null) 697f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE; 698f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return mManager.beginCopyObject(obj, parent); 699f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } 700f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang 701f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang private void endCopyObject(int handle, boolean success) { 702f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpStorageManager.MtpObject obj = mManager.getObject(handle); 703f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (obj == null || !mManager.endCopyObject(obj, success)) { 704f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang Log.e(TAG, "Failed to end copy object"); 705f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return; 706f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } 707f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (!success) { 708f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return; 709f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } 710f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang String path = obj.getPath().toString(); 711f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang int format = obj.getFormat(); 712f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang // Get parent info from MediaProvider, since the id is different from MTP's 713f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang ContentValues values = new ContentValues(); 714f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang values.put(Files.FileColumns.DATA, path); 715f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang values.put(Files.FileColumns.FORMAT, format); 716f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang values.put(Files.FileColumns.SIZE, obj.getSize()); 717f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang values.put(Files.FileColumns.DATE_MODIFIED, obj.getModifiedTime()); 718f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang try { 719f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (obj.getParent().isRoot()) { 720f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang values.put(Files.FileColumns.PARENT, 0); 721f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } else { 722f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang int parentId = findInMedia(obj.getParent().getPath()); 723f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (parentId != -1) { 724f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang values.put(Files.FileColumns.PARENT, parentId); 725f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } else { 726f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang // The parent isn't in MediaProvider. Don't add the new file. 727f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return; 728f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } 729f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } 730f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (obj.isDir()) { 731f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang mMediaScanner.scanDirectories(new String[]{path}); 732f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } else { 733f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang Uri uri = mMediaProvider.insert(mObjectsUri, values); 734f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (uri != null) { 735f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang rescanFile(path, Integer.parseInt(uri.getPathSegments().get(2)), format); 736f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } 737f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } 738f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } catch (RemoteException e) { 739f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang Log.e(TAG, "RemoteException in beginSendObject", e); 740952558d4e5a51b5f69c7c0bc1e3d7fa8da1cbf3cJerry Zhang } 741952558d4e5a51b5f69c7c0bc1e3d7fa8da1cbf3cJerry Zhang } 742952558d4e5a51b5f69c7c0bc1e3d7fa8da1cbf3cJerry Zhang 74359e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood private int setObjectProperty(int handle, int property, 744f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang long intValue, String stringValue) { 7455ebac83e20dadf10bd4a0cee4ddf69d0ec049f98Mike Lockwood switch (property) { 7465ebac83e20dadf10bd4a0cee4ddf69d0ec049f98Mike Lockwood case MtpConstants.PROPERTY_OBJECT_FILE_NAME: 7475ebac83e20dadf10bd4a0cee4ddf69d0ec049f98Mike Lockwood return renameFile(handle, stringValue); 7485ebac83e20dadf10bd4a0cee4ddf69d0ec049f98Mike Lockwood 7495ebac83e20dadf10bd4a0cee4ddf69d0ec049f98Mike Lockwood default: 7505ebac83e20dadf10bd4a0cee4ddf69d0ec049f98Mike Lockwood return MtpConstants.RESPONSE_OBJECT_PROP_NOT_SUPPORTED; 7515ebac83e20dadf10bd4a0cee4ddf69d0ec049f98Mike Lockwood } 75259e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood } 75359e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood 75459e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood private int getDeviceProperty(int property, long[] outIntValue, char[] outStringValue) { 75559e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood switch (property) { 75659e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood case MtpConstants.DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER: 75759e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood case MtpConstants.DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME: 758775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood // writable string properties kept in shared preferences 759775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood String value = mDeviceProperties.getString(Integer.toString(property), ""); 760775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood int length = value.length(); 761775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood if (length > 255) { 762775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood length = 255; 76359e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood } 764775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood value.getChars(0, length, outStringValue, 0); 765775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood outStringValue[length] = 0; 766775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood return MtpConstants.RESPONSE_OK; 767ea93fa16b166ff477e9cf6bd3ac67c8fb259fb57Mike Lockwood case MtpConstants.DEVICE_PROPERTY_IMAGE_SIZE: 768ea93fa16b166ff477e9cf6bd3ac67c8fb259fb57Mike Lockwood // use screen size as max image size 769f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang Display display = ((WindowManager) mContext.getSystemService( 770ea93fa16b166ff477e9cf6bd3ac67c8fb259fb57Mike Lockwood Context.WINDOW_SERVICE)).getDefaultDisplay(); 77144bc17c6b517aef35a390c81b5aa79c4f284f744Dianne Hackborn int width = display.getMaximumSizeDimension(); 77244bc17c6b517aef35a390c81b5aa79c4f284f744Dianne Hackborn int height = display.getMaximumSizeDimension(); 773f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang String imageSize = Integer.toString(width) + "x" + Integer.toString(height); 774ea93fa16b166ff477e9cf6bd3ac67c8fb259fb57Mike Lockwood imageSize.getChars(0, imageSize.length(), outStringValue, 0); 775ea93fa16b166ff477e9cf6bd3ac67c8fb259fb57Mike Lockwood outStringValue[imageSize.length()] = 0; 776ea93fa16b166ff477e9cf6bd3ac67c8fb259fb57Mike Lockwood return MtpConstants.RESPONSE_OK; 77713bb2f4ddadcfee8e82a0eea3cb76a2448bb2ccbJerry Zhang case MtpConstants.DEVICE_PROPERTY_PERCEIVED_DEVICE_TYPE: 77813bb2f4ddadcfee8e82a0eea3cb76a2448bb2ccbJerry Zhang outIntValue[0] = mDeviceType; 77913bb2f4ddadcfee8e82a0eea3cb76a2448bb2ccbJerry Zhang return MtpConstants.RESPONSE_OK; 780f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang case MtpConstants.DEVICE_PROPERTY_BATTERY_LEVEL: 781f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang outIntValue[0] = mBatteryLevel; 782f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang outIntValue[1] = mBatteryScale; 783f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return MtpConstants.RESPONSE_OK; 784ea93fa16b166ff477e9cf6bd3ac67c8fb259fb57Mike Lockwood default: 785ea93fa16b166ff477e9cf6bd3ac67c8fb259fb57Mike Lockwood return MtpConstants.RESPONSE_DEVICE_PROP_NOT_SUPPORTED; 786ea93fa16b166ff477e9cf6bd3ac67c8fb259fb57Mike Lockwood } 78759e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood } 78859e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood 78959e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood private int setDeviceProperty(int property, long intValue, String stringValue) { 79059e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood switch (property) { 79159e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood case MtpConstants.DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER: 79259e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood case MtpConstants.DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME: 793775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood // writable string properties kept in shared prefs 794775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood SharedPreferences.Editor e = mDeviceProperties.edit(); 795775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood e.putString(Integer.toString(property), stringValue); 796775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood return (e.commit() ? MtpConstants.RESPONSE_OK 797775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood : MtpConstants.RESPONSE_GENERAL_ERROR); 79859e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood } 79959e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood 80059e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood return MtpConstants.RESPONSE_DEVICE_PROP_NOT_SUPPORTED; 80159e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood } 80259e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood 803d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood private boolean getObjectInfo(int handle, int[] outStorageFormatParent, 804f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang char[] outName, long[] outCreatedModified) { 805f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpStorageManager.MtpObject obj = mManager.getObject(handle); 806f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (obj == null) { 807f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return false; 808d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood } 809f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang outStorageFormatParent[0] = obj.getStorageId(); 810f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang outStorageFormatParent[1] = obj.getFormat(); 811f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang outStorageFormatParent[2] = obj.getParent().isRoot() ? 0 : obj.getParent().getId(); 812f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang 813f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang int nameLen = Integer.min(obj.getName().length(), 255); 814f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang obj.getName().getChars(0, nameLen, outName, 0); 815f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang outName[nameLen] = 0; 816f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang 817f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang outCreatedModified[0] = obj.getModifiedTime(); 818f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang outCreatedModified[1] = obj.getModifiedTime(); 819f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return true; 820d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood } 821d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood 822365e03e4943b2d2f4d1e602409ab0e770ea77d1dMike Lockwood private int getObjectFilePath(int handle, char[] outFilePath, long[] outFileLengthFormat) { 823f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpStorageManager.MtpObject obj = mManager.getObject(handle); 824f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (obj == null) { 825f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE; 82601788562f26f213f63c14483fd3977e23b8314abMike Lockwood } 827f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang 828f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang String path = obj.getPath().toString(); 829f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang int pathLen = Integer.min(path.length(), 4096); 830f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang path.getChars(0, pathLen, outFilePath, 0); 831f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang outFilePath[pathLen] = 0; 832f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang 833f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang outFileLengthFormat[0] = obj.getSize(); 834f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang outFileLengthFormat[1] = obj.getFormat(); 835f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return MtpConstants.RESPONSE_OK; 836f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } 837f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang 838f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang private int getObjectFormat(int handle) { 839f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpStorageManager.MtpObject obj = mManager.getObject(handle); 840f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (obj == null) { 841f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return -1; 842f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } 843f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return obj.getFormat(); 844f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } 845f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang 846f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang private int beginDeleteObject(int handle) { 847f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpStorageManager.MtpObject obj = mManager.getObject(handle); 848f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (obj == null) { 849f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE; 850f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } 851f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (!mManager.beginRemoveObject(obj)) { 8525367ab6aab2a61aaf534b18576dcdd0bca1b9681Mike Lockwood return MtpConstants.RESPONSE_GENERAL_ERROR; 853d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood } 854f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return MtpConstants.RESPONSE_OK; 855d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood } 856d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood 857f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang private void endDeleteObject(int handle, boolean success) { 858f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpStorageManager.MtpObject obj = mManager.getObject(handle); 859f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (obj == null) { 860f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return; 861f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } 862f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (!mManager.endRemoveObject(obj, success)) 863f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang Log.e(TAG, "Failed to end remove object"); 864f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (success) 865f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang deleteFromMedia(obj.getPath(), obj.isDir()); 866f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } 867f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang 868f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang private int findInMedia(Path path) { 869f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang int ret = -1; 87071827748105252791854b87da8e0e0c66ff0d2cbMike Lockwood Cursor c = null; 87171827748105252791854b87da8e0e0c66ff0d2cbMike Lockwood try { 872f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang c = mMediaProvider.query(mObjectsUri, ID_PROJECTION, PATH_WHERE, 873f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang new String[]{path.toString()}, null, null); 87471827748105252791854b87da8e0e0c66ff0d2cbMike Lockwood if (c != null && c.moveToNext()) { 875f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang ret = c.getInt(0); 87671827748105252791854b87da8e0e0c66ff0d2cbMike Lockwood } 87771827748105252791854b87da8e0e0c66ff0d2cbMike Lockwood } catch (RemoteException e) { 878f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang Log.e(TAG, "Error finding " + path + " in MediaProvider"); 87971827748105252791854b87da8e0e0c66ff0d2cbMike Lockwood } finally { 880f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (c != null) 88171827748105252791854b87da8e0e0c66ff0d2cbMike Lockwood c.close(); 88271827748105252791854b87da8e0e0c66ff0d2cbMike Lockwood } 883f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return ret; 88471827748105252791854b87da8e0e0c66ff0d2cbMike Lockwood } 88571827748105252791854b87da8e0e0c66ff0d2cbMike Lockwood 886f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang private void deleteFromMedia(Path path, boolean isDir) { 887d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood try { 888f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang // Delete the object(s) from MediaProvider, but ignore errors. 889f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (isDir) { 89055f808cff9913a00164120a422af6ca45db5072bMike Lockwood // recursive case - delete all children first 891f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang mMediaProvider.delete(mObjectsUri, 892f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang // the 'like' makes it use the index, the 'lower()' makes it correct 893f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang // when the path contains sqlite wildcard characters 894f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang "_data LIKE ?1 AND lower(substr(_data,1,?2))=lower(?3)", 895f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang new String[]{path + "/%", Integer.toString(path.toString().length() + 1), 896f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang path.toString() + "/"}); 89755f808cff9913a00164120a422af6ca45db5072bMike Lockwood } 89855f808cff9913a00164120a422af6ca45db5072bMike Lockwood 899f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang String[] whereArgs = new String[]{path.toString()}; 900f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (mMediaProvider.delete(mObjectsUri, PATH_WHERE, whereArgs) > 0) { 901f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (!isDir && path.toString().toLowerCase(Locale.US).endsWith(NO_MEDIA)) { 902ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen try { 903f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang String parentPath = path.getParent().toString(); 90460cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey mMediaProvider.call(MediaStore.UNHIDE_CALL, parentPath, null); 905ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen } catch (RemoteException e) { 906ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen Log.e(TAG, "failed to unhide/rescan for " + path); 907ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen } 908ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen } 90959c777a24a2c639dfd84b7bf17e430e6d3424866Mike Lockwood } else { 910f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang Log.i(TAG, "Mediaprovider didn't delete " + path); 91155f808cff9913a00164120a422af6ca45db5072bMike Lockwood } 912f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } catch (Exception e) { 913f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang Log.d(TAG, "Failed to delete " + path + " from MediaProvider"); 914d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood } 915d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood } 916d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood 9179a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood private int[] getObjectReferences(int handle) { 918f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpStorageManager.MtpObject obj = mManager.getObject(handle); 919f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (obj == null) 920f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return null; 921f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang // Translate this handle to the MediaProvider Handle 922f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang handle = findInMedia(obj.getPath()); 923f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (handle == -1) 924f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return null; 9258490e66f57506d4e4b05e7c987c7ca34295843e6Mike Lockwood Uri uri = Files.getMtpReferencesUri(mVolumeName, handle); 9269a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood Cursor c = null; 9279a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood try { 928f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang c = mMediaProvider.query(uri, PATH_PROJECTION, null, null, null, null); 9299a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood if (c == null) { 9309a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood return null; 9319a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood } 932f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang ArrayList<Integer> result = new ArrayList<>(); 933f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang while (c.moveToNext()) { 934f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang // Translate result handles back into handles for this session. 935f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang String refPath = c.getString(0); 936f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpStorageManager.MtpObject refObj = mManager.getByPath(refPath); 937f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (refObj != null) { 938f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang result.add(refObj.getId()); 939f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang } 9409a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood } 941f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return result.stream().mapToInt(Integer::intValue).toArray(); 9429a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood } catch (RemoteException e) { 9439a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood Log.e(TAG, "RemoteException in getObjectList", e); 9449a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood } finally { 9459a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood if (c != null) { 9469a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood c.close(); 9479a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood } 9489a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood } 9499a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood return null; 9509a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood } 9519a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood 9529a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood private int setObjectReferences(int handle, int[] references) { 953f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpStorageManager.MtpObject obj = mManager.getObject(handle); 954f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (obj == null) 955f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE; 956f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang // Translate this handle to the MediaProvider Handle 957f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang handle = findInMedia(obj.getPath()); 958f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (handle == -1) 959f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang return MtpConstants.RESPONSE_GENERAL_ERROR; 9608490e66f57506d4e4b05e7c987c7ca34295843e6Mike Lockwood Uri uri = Files.getMtpReferencesUri(mVolumeName, handle); 961f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang ArrayList<ContentValues> valuesList = new ArrayList<>(); 962f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang for (int id : references) { 963f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang // Translate each reference id to the MediaProvider Id 964f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang MtpStorageManager.MtpObject refObj = mManager.getObject(id); 965f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (refObj == null) 966f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang continue; 967f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang int refHandle = findInMedia(refObj.getPath()); 968f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (refHandle == -1) 969f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang continue; 9709a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood ContentValues values = new ContentValues(); 971f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang values.put(Files.FileColumns._ID, refHandle); 972f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang valuesList.add(values); 9739a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood } 9749a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood try { 975f9c5c2574d95b6d233ebae8beae110f4e15c52c5Jerry Zhang if (mMediaProvider.bulkInsert(uri, valuesList.toArray(new ContentValues[0])) > 0) { 9765367ab6aab2a61aaf534b18576dcdd0bca1b9681Mike Lockwood return MtpConstants.RESPONSE_OK; 9779a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood } 9789a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood } catch (RemoteException e) { 9799a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood Log.e(TAG, "RemoteException in setObjectReferences", e); 9809a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood } 9815367ab6aab2a61aaf534b18576dcdd0bca1b9681Mike Lockwood return MtpConstants.RESPONSE_GENERAL_ERROR; 9829a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood } 9839a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood 984d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood // used by the JNI code 985e2e59326f13cf291bf5b37989390fd2a55a94f0cAshok Bhat private long mNativeContext; 986d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood 987d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood private native final void native_setup(); 988d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood private native final void native_finalize(); 989d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood} 990