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