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;
33a31560598af25d5116effc2a6af0d9be12a0628dMike Lockwoodimport android.provider.MediaStore;
349a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwoodimport android.provider.MediaStore.Audio;
353b2a62e5b35ea5f8da9d9bee52f75c9c2ebb4eceMike Lockwoodimport android.provider.MediaStore.Files;
36ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwoodimport android.provider.MediaStore.MediaColumns;
37d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwoodimport android.util.Log;
38ea93fa16b166ff477e9cf6bd3ac67c8fb259fb57Mike Lockwoodimport android.view.Display;
39ea93fa16b166ff477e9cf6bd3ac67c8fb259fb57Mike Lockwoodimport android.view.WindowManager;
40d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood
4160cfad80bdf61db436643927337c2fb30186e99dJeff Sharkeyimport dalvik.system.CloseGuard;
4260cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey
435ebac83e20dadf10bd4a0cee4ddf69d0ec049f98Mike Lockwoodimport java.io.File;
445f411696a62b7f0fb95ba3519aa127c09f155eedMarco Nelissenimport java.io.IOException;
457d7fb63071ca370f77ed69055ac30341b8388d15Mike Lockwoodimport java.util.HashMap;
46fe464a76d8fc858be971dbb5e67b1d22fd925347dujin.chaimport java.util.Locale;
4760cfad80bdf61db436643927337c2fb30186e99dJeff Sharkeyimport java.util.concurrent.atomic.AtomicBoolean;
485ebac83e20dadf10bd4a0cee4ddf69d0ec049f98Mike Lockwood
49d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood/**
50d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood * {@hide}
51d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood */
5260cfad80bdf61db436643927337c2fb30186e99dJeff Sharkeypublic class MtpDatabase implements AutoCloseable {
53d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood    private static final String TAG = "MtpDatabase";
54d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood
552837eefc5459427138c080d445bb491c75630163Mike Lockwood    private final Context mContext;
5635654b61e8fe7bc85afcb076ddbb590d51c5865fDianne Hackborn    private final String mPackageName;
5760cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey    private final ContentProviderClient mMediaProvider;
58d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood    private final String mVolumeName;
59d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood    private final Uri mObjectsUri;
6060cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey    private final MediaScanner mMediaScanner;
6160cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey
6260cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey    private final AtomicBoolean mClosed = new AtomicBoolean();
6360cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey    private final CloseGuard mCloseGuard = CloseGuard.get();
6460cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey
6573e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood    // path to primary storage
6673e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood    private final String mMediaStoragePath;
6773e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood    // if not null, restrict all queries to these subdirectories
6873e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood    private final String[] mSubDirectories;
6973e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood    // where clause for restricting queries to files in mSubDirectories
7073e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood    private String mSubDirectoriesWhere;
7173e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood    // where arguments for restricting queries to files in mSubDirectories
7273e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood    private String[] mSubDirectoriesWhereArgs;
7373e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood
74b239b683765f63d79e74b8ba2a8cc2855f236536Mike Lockwood    private final HashMap<String, MtpStorage> mStorageMap = new HashMap<String, MtpStorage>();
75d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood
767d7fb63071ca370f77ed69055ac30341b8388d15Mike Lockwood    // cached property groups for single properties
777d7fb63071ca370f77ed69055ac30341b8388d15Mike Lockwood    private final HashMap<Integer, MtpPropertyGroup> mPropertyGroupsByProperty
787d7fb63071ca370f77ed69055ac30341b8388d15Mike Lockwood            = new HashMap<Integer, MtpPropertyGroup>();
797d7fb63071ca370f77ed69055ac30341b8388d15Mike Lockwood
807d7fb63071ca370f77ed69055ac30341b8388d15Mike Lockwood    // cached property groups for all properties for a given format
817d7fb63071ca370f77ed69055ac30341b8388d15Mike Lockwood    private final HashMap<Integer, MtpPropertyGroup> mPropertyGroupsByFormat
827d7fb63071ca370f77ed69055ac30341b8388d15Mike Lockwood            = new HashMap<Integer, MtpPropertyGroup>();
837d7fb63071ca370f77ed69055ac30341b8388d15Mike Lockwood
842837eefc5459427138c080d445bb491c75630163Mike Lockwood    // true if the database has been modified in the current MTP session
852837eefc5459427138c080d445bb491c75630163Mike Lockwood    private boolean mDatabaseModified;
862837eefc5459427138c080d445bb491c75630163Mike Lockwood
87775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood    // SharedPreferences for writable MTP device properties
88775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood    private SharedPreferences mDeviceProperties;
8959e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood    private static final int DEVICE_PROPERTIES_DATABASE_VERSION = 1;
9059e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood
91d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood    private static final String[] ID_PROJECTION = new String[] {
923b2a62e5b35ea5f8da9d9bee52f75c9c2ebb4eceMike Lockwood            Files.FileColumns._ID, // 0
93d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood    };
946a6a3aff17e057eeb1457ac4d04ab4b5e8f53abeMike Lockwood    private static final String[] PATH_PROJECTION = new String[] {
955ebac83e20dadf10bd4a0cee4ddf69d0ec049f98Mike Lockwood            Files.FileColumns._ID, // 0
965ebac83e20dadf10bd4a0cee4ddf69d0ec049f98Mike Lockwood            Files.FileColumns.DATA, // 1
975ebac83e20dadf10bd4a0cee4ddf69d0ec049f98Mike Lockwood    };
9871827748105252791854b87da8e0e0c66ff0d2cbMike Lockwood    private static final String[] FORMAT_PROJECTION = new String[] {
9971827748105252791854b87da8e0e0c66ff0d2cbMike Lockwood            Files.FileColumns._ID, // 0
10071827748105252791854b87da8e0e0c66ff0d2cbMike Lockwood            Files.FileColumns.FORMAT, // 1
10171827748105252791854b87da8e0e0c66ff0d2cbMike Lockwood    };
102f6f16614574767263fcd0073f72c099edaca0607Mike Lockwood    private static final String[] PATH_FORMAT_PROJECTION = new String[] {
1033b2a62e5b35ea5f8da9d9bee52f75c9c2ebb4eceMike Lockwood            Files.FileColumns._ID, // 0
1043b2a62e5b35ea5f8da9d9bee52f75c9c2ebb4eceMike Lockwood            Files.FileColumns.DATA, // 1
105f6f16614574767263fcd0073f72c099edaca0607Mike Lockwood            Files.FileColumns.FORMAT, // 2
106d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood    };
107d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood    private static final String[] OBJECT_INFO_PROJECTION = new String[] {
1083b2a62e5b35ea5f8da9d9bee52f75c9c2ebb4eceMike Lockwood            Files.FileColumns._ID, // 0
109b239b683765f63d79e74b8ba2a8cc2855f236536Mike Lockwood            Files.FileColumns.STORAGE_ID, // 1
1103b2a62e5b35ea5f8da9d9bee52f75c9c2ebb4eceMike Lockwood            Files.FileColumns.FORMAT, // 2
1113b2a62e5b35ea5f8da9d9bee52f75c9c2ebb4eceMike Lockwood            Files.FileColumns.PARENT, // 3
112b239b683765f63d79e74b8ba2a8cc2855f236536Mike Lockwood            Files.FileColumns.DATA, // 4
1131341f1eee3493fc5e263494acb8adebb6f0180b6Mike Lockwood            Files.FileColumns.DATE_ADDED, // 5
1141341f1eee3493fc5e263494acb8adebb6f0180b6Mike Lockwood            Files.FileColumns.DATE_MODIFIED, // 6
115d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood    };
1163b2a62e5b35ea5f8da9d9bee52f75c9c2ebb4eceMike Lockwood    private static final String ID_WHERE = Files.FileColumns._ID + "=?";
117bafca214eaf326441e5157d645b2ee7fe8c7d1abMike Lockwood    private static final String PATH_WHERE = Files.FileColumns.DATA + "=?";
1186acc90fc216530f0a5bbf7339066af9b415cbb02Mike Lockwood
1196acc90fc216530f0a5bbf7339066af9b415cbb02Mike Lockwood    private static final String STORAGE_WHERE = Files.FileColumns.STORAGE_ID + "=?";
12058e6831ccb54b33a914115e88ebb67e720615fb8Mike Lockwood    private static final String FORMAT_WHERE = Files.FileColumns.FORMAT + "=?";
12158e6831ccb54b33a914115e88ebb67e720615fb8Mike Lockwood    private static final String PARENT_WHERE = Files.FileColumns.PARENT + "=?";
1226acc90fc216530f0a5bbf7339066af9b415cbb02Mike Lockwood    private static final String STORAGE_FORMAT_WHERE = STORAGE_WHERE + " AND "
123b239b683765f63d79e74b8ba2a8cc2855f236536Mike Lockwood                                            + Files.FileColumns.FORMAT + "=?";
1246acc90fc216530f0a5bbf7339066af9b415cbb02Mike Lockwood    private static final String STORAGE_PARENT_WHERE = STORAGE_WHERE + " AND "
1256acc90fc216530f0a5bbf7339066af9b415cbb02Mike Lockwood                                            + Files.FileColumns.PARENT + "=?";
1266acc90fc216530f0a5bbf7339066af9b415cbb02Mike Lockwood    private static final String FORMAT_PARENT_WHERE = FORMAT_WHERE + " AND "
1276acc90fc216530f0a5bbf7339066af9b415cbb02Mike Lockwood                                            + Files.FileColumns.PARENT + "=?";
1286acc90fc216530f0a5bbf7339066af9b415cbb02Mike Lockwood    private static final String STORAGE_FORMAT_PARENT_WHERE = STORAGE_FORMAT_WHERE + " AND "
1296acc90fc216530f0a5bbf7339066af9b415cbb02Mike Lockwood                                            + Files.FileColumns.PARENT + "=?";
130d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood
13156c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood    private MtpServer mServer;
13256c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood
13356c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood    // read from native code
13456c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood    private int mBatteryLevel;
13556c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood    private int mBatteryScale;
136d815f79766984fce499e147ecbacc01914683f74Mike Lockwood
13713bb2f4ddadcfee8e82a0eea3cb76a2448bb2ccbJerry Zhang    private int mDeviceType;
13813bb2f4ddadcfee8e82a0eea3cb76a2448bb2ccbJerry Zhang
139d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood    static {
140d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood        System.loadLibrary("media_jni");
141d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood    }
142d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood
14356c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood    private BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() {
14456c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood          @Override
14556c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood        public void onReceive(Context context, Intent intent) {
14656c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood            String action = intent.getAction();
14756c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood            if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
14856c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood                mBatteryScale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 0);
14956c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood                int newLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
15056c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood                if (newLevel != mBatteryLevel) {
15156c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood                    mBatteryLevel = newLevel;
15256c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood                    if (mServer != null) {
15356c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood                        // send device property changed event
15456c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood                        mServer.sendDevicePropertyChanged(
15556c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood                                MtpConstants.DEVICE_PROPERTY_BATTERY_LEVEL);
15656c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood                    }
15756c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood                }
15856c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood            }
15956c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood        }
16056c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood    };
16156c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood
16273e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood    public MtpDatabase(Context context, String volumeName, String storagePath,
16373e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood            String[] subDirectories) {
164d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood        native_setup();
165d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood
1662837eefc5459427138c080d445bb491c75630163Mike Lockwood        mContext = context;
16735654b61e8fe7bc85afcb076ddbb590d51c5865fDianne Hackborn        mPackageName = context.getPackageName();
16860cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey        mMediaProvider = context.getContentResolver()
16960cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey                .acquireContentProviderClient(MediaStore.AUTHORITY);
170d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood        mVolumeName = volumeName;
17101788562f26f213f63c14483fd3977e23b8314abMike Lockwood        mMediaStoragePath = storagePath;
1728490e66f57506d4e4b05e7c987c7ca34295843e6Mike Lockwood        mObjectsUri = Files.getMtpObjectsUri(volumeName);
17360cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey        mMediaScanner = new MediaScanner(context, mVolumeName);
174fe464a76d8fc858be971dbb5e67b1d22fd925347dujin.cha
17573e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood        mSubDirectories = subDirectories;
17673e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood        if (subDirectories != null) {
17773e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood            // Compute "where" string for restricting queries to subdirectories
17873e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood            StringBuilder builder = new StringBuilder();
17973e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood            builder.append("(");
18073e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood            int count = subDirectories.length;
18173e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood            for (int i = 0; i < count; i++) {
18273e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                builder.append(Files.FileColumns.DATA + "=? OR "
18373e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                        + Files.FileColumns.DATA + " LIKE ?");
18473e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                if (i != count - 1) {
18573e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                    builder.append(" OR ");
18673e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                }
18773e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood            }
18873e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood            builder.append(")");
18973e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood            mSubDirectoriesWhere = builder.toString();
19073e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood
19173e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood            // Compute "where" arguments for restricting queries to subdirectories
19273e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood            mSubDirectoriesWhereArgs = new String[count * 2];
19373e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood            for (int i = 0, j = 0; i < count; i++) {
19473e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                String path = subDirectories[i];
19573e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                mSubDirectoriesWhereArgs[j++] = path;
19673e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                mSubDirectoriesWhereArgs[j++] = path + "/%";
19773e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood            }
19873e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood        }
19973e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood
200775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood        initDeviceProperties(context);
20113bb2f4ddadcfee8e82a0eea3cb76a2448bb2ccbJerry Zhang        mDeviceType = SystemProperties.getInt("sys.usb.mtp.device_type", 0);
20260cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey
20360cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey        mCloseGuard.open("close");
204d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood    }
205d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood
20656c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood    public void setServer(MtpServer server) {
20756c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood        mServer = server;
20856c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood
2091632fae376eb460cf992e543b13c8690b71fe8d5Marco Nelissen        // always unregister before registering
2101632fae376eb460cf992e543b13c8690b71fe8d5Marco Nelissen        try {
2111632fae376eb460cf992e543b13c8690b71fe8d5Marco Nelissen            mContext.unregisterReceiver(mBatteryReceiver);
2121632fae376eb460cf992e543b13c8690b71fe8d5Marco Nelissen        } catch (IllegalArgumentException e) {
2131632fae376eb460cf992e543b13c8690b71fe8d5Marco Nelissen            // wasn't previously registered, ignore
2141632fae376eb460cf992e543b13c8690b71fe8d5Marco Nelissen        }
2151632fae376eb460cf992e543b13c8690b71fe8d5Marco Nelissen
21656c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood        // register for battery notifications when we are connected
21756c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood        if (server != null) {
21856c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood            mContext.registerReceiver(mBatteryReceiver,
21956c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood                    new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
22056c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood        }
22156c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood    }
22256c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood
223d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood    @Override
22460cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey    public void close() {
22560cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey        mCloseGuard.close();
22660cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey        if (mClosed.compareAndSet(false, true)) {
22760cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey            mMediaScanner.close();
22860cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey            mMediaProvider.close();
22960cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey            native_finalize();
23060cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey        }
23160cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey    }
23260cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey
23360cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey    @Override
234dbead321daf2c16dc74e182bec591b278cfccb2cMike Lockwood    protected void finalize() throws Throwable {
235dbead321daf2c16dc74e182bec591b278cfccb2cMike Lockwood        try {
23660cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey            mCloseGuard.warnIfOpen();
23760cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey            close();
238dbead321daf2c16dc74e182bec591b278cfccb2cMike Lockwood        } finally {
239dbead321daf2c16dc74e182bec591b278cfccb2cMike Lockwood            super.finalize();
240dbead321daf2c16dc74e182bec591b278cfccb2cMike Lockwood        }
241d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood    }
242d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood
243b239b683765f63d79e74b8ba2a8cc2855f236536Mike Lockwood    public void addStorage(MtpStorage storage) {
244b239b683765f63d79e74b8ba2a8cc2855f236536Mike Lockwood        mStorageMap.put(storage.getPath(), storage);
245b239b683765f63d79e74b8ba2a8cc2855f236536Mike Lockwood    }
246b239b683765f63d79e74b8ba2a8cc2855f236536Mike Lockwood
247b239b683765f63d79e74b8ba2a8cc2855f236536Mike Lockwood    public void removeStorage(MtpStorage storage) {
248b239b683765f63d79e74b8ba2a8cc2855f236536Mike Lockwood        mStorageMap.remove(storage.getPath());
249b239b683765f63d79e74b8ba2a8cc2855f236536Mike Lockwood    }
250b239b683765f63d79e74b8ba2a8cc2855f236536Mike Lockwood
251775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood    private void initDeviceProperties(Context context) {
252775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood        final String devicePropertiesName = "device-properties";
253775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood        mDeviceProperties = context.getSharedPreferences(devicePropertiesName, Context.MODE_PRIVATE);
254775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood        File databaseFile = context.getDatabasePath(devicePropertiesName);
255775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood
256775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood        if (databaseFile.exists()) {
257775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood            // for backward compatibility - read device properties from sqlite database
258775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood            // and migrate them to shared prefs
259775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood            SQLiteDatabase db = null;
260775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood            Cursor c = null;
261775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood            try {
262775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood                db = context.openOrCreateDatabase("device-properties", Context.MODE_PRIVATE, null);
263775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood                if (db != null) {
264775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood                    c = db.query("properties", new String[] { "_id", "code", "value" },
265775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood                            null, null, null, null, null);
266775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood                    if (c != null) {
267775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood                        SharedPreferences.Editor e = mDeviceProperties.edit();
268775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood                        while (c.moveToNext()) {
269775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood                            String name = c.getString(1);
270775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood                            String value = c.getString(2);
271775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood                            e.putString(name, value);
272775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood                        }
273775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood                        e.commit();
274775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood                    }
275775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood                }
276775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood            } catch (Exception e) {
277775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood                Log.e(TAG, "failed to migrate device properties", e);
278775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood            } finally {
279775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood                if (c != null) c.close();
280775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood                if (db != null) db.close();
281775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood            }
2823ed02535fdc3c752346c40b57962905b7072e693jangwon.lee            context.deleteDatabase(devicePropertiesName);
28359e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood        }
28459e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood    }
28559e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood
28673e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood    // check to see if the path is contained in one of our storage subdirectories
28773e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood    // returns true if we have no special subdirectories
28873e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood    private boolean inStorageSubDirectory(String path) {
28973e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood        if (mSubDirectories == null) return true;
29073e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood        if (path == null) return false;
29173e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood
29273e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood        boolean allowed = false;
29373e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood        int pathLength = path.length();
29473e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood        for (int i = 0; i < mSubDirectories.length && !allowed; i++) {
29573e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood            String subdir = mSubDirectories[i];
29673e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood            int subdirLength = subdir.length();
29773e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood            if (subdirLength < pathLength &&
29873e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                    path.charAt(subdirLength) == '/' &&
29973e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                    path.startsWith(subdir)) {
30073e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                allowed = true;
30173e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood            }
30273e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood        }
30373e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood        return allowed;
30473e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood    }
30573e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood
30673e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood    // check to see if the path matches one of our storage subdirectories
30773e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood    // returns true if we have no special subdirectories
30873e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood    private boolean isStorageSubDirectory(String path) {
30973e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood    if (mSubDirectories == null) return false;
31073e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood        for (int i = 0; i < mSubDirectories.length; i++) {
31173e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood            if (path.equals(mSubDirectories[i])) {
31273e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                return true;
31373e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood            }
31473e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood        }
31573e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood        return false;
31673e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood    }
31773e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood
3185f411696a62b7f0fb95ba3519aa127c09f155eedMarco Nelissen    // returns true if the path is in the storage root
3195f411696a62b7f0fb95ba3519aa127c09f155eedMarco Nelissen    private boolean inStorageRoot(String path) {
3205f411696a62b7f0fb95ba3519aa127c09f155eedMarco Nelissen        try {
3215f411696a62b7f0fb95ba3519aa127c09f155eedMarco Nelissen            File f = new File(path);
3225f411696a62b7f0fb95ba3519aa127c09f155eedMarco Nelissen            String canonical = f.getCanonicalPath();
323c1fda12823bfac1c2180beddc3f733ea0311b37dMarco Nelissen            for (String root: mStorageMap.keySet()) {
324c1fda12823bfac1c2180beddc3f733ea0311b37dMarco Nelissen                if (canonical.startsWith(root)) {
325c1fda12823bfac1c2180beddc3f733ea0311b37dMarco Nelissen                    return true;
326c1fda12823bfac1c2180beddc3f733ea0311b37dMarco Nelissen                }
3275f411696a62b7f0fb95ba3519aa127c09f155eedMarco Nelissen            }
3285f411696a62b7f0fb95ba3519aa127c09f155eedMarco Nelissen        } catch (IOException e) {
3295f411696a62b7f0fb95ba3519aa127c09f155eedMarco Nelissen            // ignore
3305f411696a62b7f0fb95ba3519aa127c09f155eedMarco Nelissen        }
3315f411696a62b7f0fb95ba3519aa127c09f155eedMarco Nelissen        return false;
3325f411696a62b7f0fb95ba3519aa127c09f155eedMarco Nelissen    }
3335f411696a62b7f0fb95ba3519aa127c09f155eedMarco Nelissen
334d815f79766984fce499e147ecbacc01914683f74Mike Lockwood    private int beginSendObject(String path, int format, int parent,
335b239b683765f63d79e74b8ba2a8cc2855f236536Mike Lockwood                         int storageId, long size, long modified) {
3365f411696a62b7f0fb95ba3519aa127c09f155eedMarco Nelissen        // if the path is outside of the storage root, do not allow access
3375f411696a62b7f0fb95ba3519aa127c09f155eedMarco Nelissen        if (!inStorageRoot(path)) {
3385f411696a62b7f0fb95ba3519aa127c09f155eedMarco Nelissen            Log.e(TAG, "attempt to put file outside of storage area: " + path);
3395f411696a62b7f0fb95ba3519aa127c09f155eedMarco Nelissen            return -1;
3405f411696a62b7f0fb95ba3519aa127c09f155eedMarco Nelissen        }
34173e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood        // if mSubDirectories is not null, do not allow copying files to any other locations
34273e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood        if (!inStorageSubDirectory(path)) return -1;
34373e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood
34473e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood        // make sure the object does not exist
345bafca214eaf326441e5157d645b2ee7fe8c7d1abMike Lockwood        if (path != null) {
346bafca214eaf326441e5157d645b2ee7fe8c7d1abMike Lockwood            Cursor c = null;
347bafca214eaf326441e5157d645b2ee7fe8c7d1abMike Lockwood            try {
34860cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey                c = mMediaProvider.query(mObjectsUri, ID_PROJECTION, PATH_WHERE,
34975ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                        new String[] { path }, null, null);
350bafca214eaf326441e5157d645b2ee7fe8c7d1abMike Lockwood                if (c != null && c.getCount() > 0) {
351bafca214eaf326441e5157d645b2ee7fe8c7d1abMike Lockwood                    Log.w(TAG, "file already exists in beginSendObject: " + path);
352bafca214eaf326441e5157d645b2ee7fe8c7d1abMike Lockwood                    return -1;
353bafca214eaf326441e5157d645b2ee7fe8c7d1abMike Lockwood                }
354bafca214eaf326441e5157d645b2ee7fe8c7d1abMike Lockwood            } catch (RemoteException e) {
355bafca214eaf326441e5157d645b2ee7fe8c7d1abMike Lockwood                Log.e(TAG, "RemoteException in beginSendObject", e);
356bafca214eaf326441e5157d645b2ee7fe8c7d1abMike Lockwood            } finally {
357bafca214eaf326441e5157d645b2ee7fe8c7d1abMike Lockwood                if (c != null) {
358bafca214eaf326441e5157d645b2ee7fe8c7d1abMike Lockwood                    c.close();
359bafca214eaf326441e5157d645b2ee7fe8c7d1abMike Lockwood                }
360bafca214eaf326441e5157d645b2ee7fe8c7d1abMike Lockwood            }
361bafca214eaf326441e5157d645b2ee7fe8c7d1abMike Lockwood        }
362bafca214eaf326441e5157d645b2ee7fe8c7d1abMike Lockwood
3632837eefc5459427138c080d445bb491c75630163Mike Lockwood        mDatabaseModified = true;
364d815f79766984fce499e147ecbacc01914683f74Mike Lockwood        ContentValues values = new ContentValues();
3653b2a62e5b35ea5f8da9d9bee52f75c9c2ebb4eceMike Lockwood        values.put(Files.FileColumns.DATA, path);
3663b2a62e5b35ea5f8da9d9bee52f75c9c2ebb4eceMike Lockwood        values.put(Files.FileColumns.FORMAT, format);
3673b2a62e5b35ea5f8da9d9bee52f75c9c2ebb4eceMike Lockwood        values.put(Files.FileColumns.PARENT, parent);
368b239b683765f63d79e74b8ba2a8cc2855f236536Mike Lockwood        values.put(Files.FileColumns.STORAGE_ID, storageId);
3693b2a62e5b35ea5f8da9d9bee52f75c9c2ebb4eceMike Lockwood        values.put(Files.FileColumns.SIZE, size);
3703b2a62e5b35ea5f8da9d9bee52f75c9c2ebb4eceMike Lockwood        values.put(Files.FileColumns.DATE_MODIFIED, modified);
371d815f79766984fce499e147ecbacc01914683f74Mike Lockwood
372d815f79766984fce499e147ecbacc01914683f74Mike Lockwood        try {
37360cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey            Uri uri = mMediaProvider.insert(mObjectsUri, values);
374d815f79766984fce499e147ecbacc01914683f74Mike Lockwood            if (uri != null) {
375d815f79766984fce499e147ecbacc01914683f74Mike Lockwood                return Integer.parseInt(uri.getPathSegments().get(2));
376d815f79766984fce499e147ecbacc01914683f74Mike Lockwood            } else {
377d815f79766984fce499e147ecbacc01914683f74Mike Lockwood                return -1;
378d815f79766984fce499e147ecbacc01914683f74Mike Lockwood            }
379d815f79766984fce499e147ecbacc01914683f74Mike Lockwood        } catch (RemoteException e) {
380d815f79766984fce499e147ecbacc01914683f74Mike Lockwood            Log.e(TAG, "RemoteException in beginSendObject", e);
381d815f79766984fce499e147ecbacc01914683f74Mike Lockwood            return -1;
382d815f79766984fce499e147ecbacc01914683f74Mike Lockwood        }
383d815f79766984fce499e147ecbacc01914683f74Mike Lockwood    }
384d815f79766984fce499e147ecbacc01914683f74Mike Lockwood
3857a0bd17bceaf3efc3732e30c538fae420d3b742cMike Lockwood    private void endSendObject(String path, int handle, int format, boolean succeeded) {
386d815f79766984fce499e147ecbacc01914683f74Mike Lockwood        if (succeeded) {
3879a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood            // handle abstract playlists separately
3889a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood            // they do not exist in the file system so don't use the media scanner here
3895367ab6aab2a61aaf534b18576dcdd0bca1b9681Mike Lockwood            if (format == MtpConstants.FORMAT_ABSTRACT_AV_PLAYLIST) {
3909a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood                // extract name from path
3919a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood                String name = path;
3929a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood                int lastSlash = name.lastIndexOf('/');
3939a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood                if (lastSlash >= 0) {
3949a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood                    name = name.substring(lastSlash + 1);
3959a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood                }
3968cc6eb1e4c708fb4398292ba30aa80ad5770d7ceMike Lockwood                // strip trailing ".pla" from the name
3978cc6eb1e4c708fb4398292ba30aa80ad5770d7ceMike Lockwood                if (name.endsWith(".pla")) {
3988cc6eb1e4c708fb4398292ba30aa80ad5770d7ceMike Lockwood                    name = name.substring(0, name.length() - 4);
3998cc6eb1e4c708fb4398292ba30aa80ad5770d7ceMike Lockwood                }
4009a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood
4019a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood                ContentValues values = new ContentValues(1);
4029a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood                values.put(Audio.Playlists.DATA, path);
4039a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood                values.put(Audio.Playlists.NAME, name);
4040b58c19840c577c8d8fd8aa5315676d61b78ba0fMike Lockwood                values.put(Files.FileColumns.FORMAT, format);
4058ed67acf38ccdfdb89553ed58620a135f2100e10Mike Lockwood                values.put(Files.FileColumns.DATE_MODIFIED, System.currentTimeMillis() / 1000);
4069a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood                values.put(MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID, handle);
4079a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood                try {
40860cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey                    Uri uri = mMediaProvider.insert(
40935654b61e8fe7bc85afcb076ddbb590d51c5865fDianne Hackborn                            Audio.Playlists.EXTERNAL_CONTENT_URI, values);
4109a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood                } catch (RemoteException e) {
4119a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood                    Log.e(TAG, "RemoteException in endSendObject", e);
4129a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood                }
4139a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood            } else {
41460cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey                mMediaScanner.scanMtpFile(path, handle, format);
4159a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood            }
416d815f79766984fce499e147ecbacc01914683f74Mike Lockwood        } else {
417d815f79766984fce499e147ecbacc01914683f74Mike Lockwood            deleteFile(handle);
418d815f79766984fce499e147ecbacc01914683f74Mike Lockwood        }
419d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood    }
420d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood
421b239b683765f63d79e74b8ba2a8cc2855f236536Mike Lockwood    private Cursor createObjectQuery(int storageID, int format, int parent) throws RemoteException {
42273e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood        String where;
42373e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood        String[] whereArgs;
42473e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood
4256acc90fc216530f0a5bbf7339066af9b415cbb02Mike Lockwood        if (storageID == 0xFFFFFFFF) {
4266acc90fc216530f0a5bbf7339066af9b415cbb02Mike Lockwood            // query all stores
4276acc90fc216530f0a5bbf7339066af9b415cbb02Mike Lockwood            if (format == 0) {
4286acc90fc216530f0a5bbf7339066af9b415cbb02Mike Lockwood                // query all formats
4296acc90fc216530f0a5bbf7339066af9b415cbb02Mike Lockwood                if (parent == 0) {
4306acc90fc216530f0a5bbf7339066af9b415cbb02Mike Lockwood                    // query all objects
43173e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                    where = null;
43273e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                    whereArgs = null;
43373e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                } else {
43473e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                    if (parent == 0xFFFFFFFF) {
43573e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                        // all objects in root of store
43673e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                        parent = 0;
43773e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                    }
43873e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                    where = PARENT_WHERE;
43973e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                    whereArgs = new String[] { Integer.toString(parent) };
4406acc90fc216530f0a5bbf7339066af9b415cbb02Mike Lockwood                }
441b239b683765f63d79e74b8ba2a8cc2855f236536Mike Lockwood            } else {
4426acc90fc216530f0a5bbf7339066af9b415cbb02Mike Lockwood                // query specific format
4436acc90fc216530f0a5bbf7339066af9b415cbb02Mike Lockwood                if (parent == 0) {
4446acc90fc216530f0a5bbf7339066af9b415cbb02Mike Lockwood                    // query all objects
44573e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                    where = FORMAT_WHERE;
44673e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                    whereArgs = new String[] { Integer.toString(format) };
44773e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                } else {
44873e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                    if (parent == 0xFFFFFFFF) {
44973e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                        // all objects in root of store
45073e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                        parent = 0;
45173e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                    }
45273e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                    where = FORMAT_PARENT_WHERE;
45373e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                    whereArgs = new String[] { Integer.toString(format),
45473e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                                               Integer.toString(parent) };
4556acc90fc216530f0a5bbf7339066af9b415cbb02Mike Lockwood                }
456b239b683765f63d79e74b8ba2a8cc2855f236536Mike Lockwood            }
457b239b683765f63d79e74b8ba2a8cc2855f236536Mike Lockwood        } else {
4586acc90fc216530f0a5bbf7339066af9b415cbb02Mike Lockwood            // query specific store
4596acc90fc216530f0a5bbf7339066af9b415cbb02Mike Lockwood            if (format == 0) {
4606acc90fc216530f0a5bbf7339066af9b415cbb02Mike Lockwood                // query all formats
4616acc90fc216530f0a5bbf7339066af9b415cbb02Mike Lockwood                if (parent == 0) {
4626acc90fc216530f0a5bbf7339066af9b415cbb02Mike Lockwood                    // query all objects
46373e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                    where = STORAGE_WHERE;
46473e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                    whereArgs = new String[] { Integer.toString(storageID) };
46573e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                } else {
46673e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                    if (parent == 0xFFFFFFFF) {
46773e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                        // all objects in root of store
46873e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                        parent = 0;
46973e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                    }
47073e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                    where = STORAGE_PARENT_WHERE;
47173e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                    whereArgs = new String[] { Integer.toString(storageID),
47273e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                                               Integer.toString(parent) };
4736acc90fc216530f0a5bbf7339066af9b415cbb02Mike Lockwood                }
474d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood            } else {
4756acc90fc216530f0a5bbf7339066af9b415cbb02Mike Lockwood                // query specific format
4766acc90fc216530f0a5bbf7339066af9b415cbb02Mike Lockwood                if (parent == 0) {
4776acc90fc216530f0a5bbf7339066af9b415cbb02Mike Lockwood                    // query all objects
47873e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                    where = STORAGE_FORMAT_WHERE;
47973e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                    whereArgs = new String[] {  Integer.toString(storageID),
48073e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                                                Integer.toString(format) };
48173e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                } else {
48273e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                    if (parent == 0xFFFFFFFF) {
48373e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                        // all objects in root of store
48473e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                        parent = 0;
48573e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                    }
48673e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                    where = STORAGE_FORMAT_PARENT_WHERE;
48773e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                    whereArgs = new String[] { Integer.toString(storageID),
48873e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                                               Integer.toString(format),
48973e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                                               Integer.toString(parent) };
4906acc90fc216530f0a5bbf7339066af9b415cbb02Mike Lockwood                }
49173e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood            }
49273e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood        }
49373e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood
49473e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood        // if we are restricting queries to mSubDirectories, we need to add the restriction
49573e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood        // onto our "where" arguments
49673e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood        if (mSubDirectoriesWhere != null) {
49773e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood            if (where == null) {
49873e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                where = mSubDirectoriesWhere;
49973e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                whereArgs = mSubDirectoriesWhereArgs;
50073e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood            } else {
50173e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                where = where + " AND " + mSubDirectoriesWhere;
50273e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood
50373e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                // create new array to hold whereArgs and mSubDirectoriesWhereArgs
50473e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                String[] newWhereArgs =
50573e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                        new String[whereArgs.length + mSubDirectoriesWhereArgs.length];
50673e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                int i, j;
50773e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                for (i = 0; i < whereArgs.length; i++) {
50873e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                    newWhereArgs[i] = whereArgs[i];
5096acc90fc216530f0a5bbf7339066af9b415cbb02Mike Lockwood                }
51073e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                for (j = 0; j < mSubDirectoriesWhereArgs.length; i++, j++) {
51173e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                    newWhereArgs[i] = mSubDirectoriesWhereArgs[j];
51273e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                }
51373e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                whereArgs = newWhereArgs;
514d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood            }
515b239b683765f63d79e74b8ba2a8cc2855f236536Mike Lockwood        }
51673e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood
51760cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey        return mMediaProvider.query(mObjectsUri, ID_PROJECTION, where,
51835654b61e8fe7bc85afcb076ddbb590d51c5865fDianne Hackborn                whereArgs, null, null);
519b239b683765f63d79e74b8ba2a8cc2855f236536Mike Lockwood    }
520b239b683765f63d79e74b8ba2a8cc2855f236536Mike Lockwood
521b239b683765f63d79e74b8ba2a8cc2855f236536Mike Lockwood    private int[] getObjectList(int storageID, int format, int parent) {
522b239b683765f63d79e74b8ba2a8cc2855f236536Mike Lockwood        Cursor c = null;
523b239b683765f63d79e74b8ba2a8cc2855f236536Mike Lockwood        try {
524b239b683765f63d79e74b8ba2a8cc2855f236536Mike Lockwood            c = createObjectQuery(storageID, format, parent);
525d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood            if (c == null) {
526d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood                return null;
527d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood            }
528d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood            int count = c.getCount();
529d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood            if (count > 0) {
530d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood                int[] result = new int[count];
531d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood                for (int i = 0; i < count; i++) {
532d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood                    c.moveToNext();
533d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood                    result[i] = c.getInt(0);
534d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood                }
535d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood                return result;
536d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood            }
537d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood        } catch (RemoteException e) {
538d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood            Log.e(TAG, "RemoteException in getObjectList", e);
539d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood        } finally {
540d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood            if (c != null) {
541d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood                c.close();
542d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood            }
543d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood        }
544d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood        return null;
545d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood    }
546d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood
5477a047c89862febf63ce1bb5873179c7a2ff8d9d3Mike Lockwood    private int getNumObjects(int storageID, int format, int parent) {
5487a047c89862febf63ce1bb5873179c7a2ff8d9d3Mike Lockwood        Cursor c = null;
5497a047c89862febf63ce1bb5873179c7a2ff8d9d3Mike Lockwood        try {
550b239b683765f63d79e74b8ba2a8cc2855f236536Mike Lockwood            c = createObjectQuery(storageID, format, parent);
5517a047c89862febf63ce1bb5873179c7a2ff8d9d3Mike Lockwood            if (c != null) {
5527a047c89862febf63ce1bb5873179c7a2ff8d9d3Mike Lockwood                return c.getCount();
5537a047c89862febf63ce1bb5873179c7a2ff8d9d3Mike Lockwood            }
5547a047c89862febf63ce1bb5873179c7a2ff8d9d3Mike Lockwood        } catch (RemoteException e) {
5557a047c89862febf63ce1bb5873179c7a2ff8d9d3Mike Lockwood            Log.e(TAG, "RemoteException in getNumObjects", e);
5567a047c89862febf63ce1bb5873179c7a2ff8d9d3Mike Lockwood        } finally {
5577a047c89862febf63ce1bb5873179c7a2ff8d9d3Mike Lockwood            if (c != null) {
5587a047c89862febf63ce1bb5873179c7a2ff8d9d3Mike Lockwood                c.close();
5597a047c89862febf63ce1bb5873179c7a2ff8d9d3Mike Lockwood            }
5607a047c89862febf63ce1bb5873179c7a2ff8d9d3Mike Lockwood        }
5617a047c89862febf63ce1bb5873179c7a2ff8d9d3Mike Lockwood        return -1;
5627a047c89862febf63ce1bb5873179c7a2ff8d9d3Mike Lockwood    }
5637a047c89862febf63ce1bb5873179c7a2ff8d9d3Mike Lockwood
5644b322ce4fb86b5a7a6e50633a3a9f498ca8d4706Mike Lockwood    private int[] getSupportedPlaybackFormats() {
5654b322ce4fb86b5a7a6e50633a3a9f498ca8d4706Mike Lockwood        return new int[] {
566e521169ff5dc6cbe1762bd7a049d95b56cc657eeMike Lockwood            // allow transfering arbitrary files
567e521169ff5dc6cbe1762bd7a049d95b56cc657eeMike Lockwood            MtpConstants.FORMAT_UNDEFINED,
56812b8a99fc1d4acc427307a4a924bf33745410260Mike Lockwood
569792ec849e5bc27c090c62f578846b888fa43e0d6Mike Lockwood            MtpConstants.FORMAT_ASSOCIATION,
57012b8a99fc1d4acc427307a4a924bf33745410260Mike Lockwood            MtpConstants.FORMAT_TEXT,
57112b8a99fc1d4acc427307a4a924bf33745410260Mike Lockwood            MtpConstants.FORMAT_HTML,
57212b8a99fc1d4acc427307a4a924bf33745410260Mike Lockwood            MtpConstants.FORMAT_WAV,
57312b8a99fc1d4acc427307a4a924bf33745410260Mike Lockwood            MtpConstants.FORMAT_MP3,
57412b8a99fc1d4acc427307a4a924bf33745410260Mike Lockwood            MtpConstants.FORMAT_MPEG,
57512b8a99fc1d4acc427307a4a924bf33745410260Mike Lockwood            MtpConstants.FORMAT_EXIF_JPEG,
57612b8a99fc1d4acc427307a4a924bf33745410260Mike Lockwood            MtpConstants.FORMAT_TIFF_EP,
577240582eabee56d55751b85e220811d48fabe839abo huang            MtpConstants.FORMAT_BMP,
57812b8a99fc1d4acc427307a4a924bf33745410260Mike Lockwood            MtpConstants.FORMAT_GIF,
57912b8a99fc1d4acc427307a4a924bf33745410260Mike Lockwood            MtpConstants.FORMAT_JFIF,
58012b8a99fc1d4acc427307a4a924bf33745410260Mike Lockwood            MtpConstants.FORMAT_PNG,
58112b8a99fc1d4acc427307a4a924bf33745410260Mike Lockwood            MtpConstants.FORMAT_TIFF,
58212b8a99fc1d4acc427307a4a924bf33745410260Mike Lockwood            MtpConstants.FORMAT_WMA,
58312b8a99fc1d4acc427307a4a924bf33745410260Mike Lockwood            MtpConstants.FORMAT_OGG,
58412b8a99fc1d4acc427307a4a924bf33745410260Mike Lockwood            MtpConstants.FORMAT_AAC,
58512b8a99fc1d4acc427307a4a924bf33745410260Mike Lockwood            MtpConstants.FORMAT_MP4_CONTAINER,
58612b8a99fc1d4acc427307a4a924bf33745410260Mike Lockwood            MtpConstants.FORMAT_MP2,
58712b8a99fc1d4acc427307a4a924bf33745410260Mike Lockwood            MtpConstants.FORMAT_3GP_CONTAINER,
588792ec849e5bc27c090c62f578846b888fa43e0d6Mike Lockwood            MtpConstants.FORMAT_ABSTRACT_AV_PLAYLIST,
58912b8a99fc1d4acc427307a4a924bf33745410260Mike Lockwood            MtpConstants.FORMAT_WPL_PLAYLIST,
59012b8a99fc1d4acc427307a4a924bf33745410260Mike Lockwood            MtpConstants.FORMAT_M3U_PLAYLIST,
59112b8a99fc1d4acc427307a4a924bf33745410260Mike Lockwood            MtpConstants.FORMAT_PLS_PLAYLIST,
59212b8a99fc1d4acc427307a4a924bf33745410260Mike Lockwood            MtpConstants.FORMAT_XML_DOCUMENT,
593f9f223edb408cdc9fd946dc099a86857079a639fGlenn Kasten            MtpConstants.FORMAT_FLAC,
5945a8b9627c7fdacc94791fb3024386739f8cf9a56Jaesung Chung            MtpConstants.FORMAT_DNG,
5954b322ce4fb86b5a7a6e50633a3a9f498ca8d4706Mike Lockwood        };
5964b322ce4fb86b5a7a6e50633a3a9f498ca8d4706Mike Lockwood    }
5974b322ce4fb86b5a7a6e50633a3a9f498ca8d4706Mike Lockwood
5984b322ce4fb86b5a7a6e50633a3a9f498ca8d4706Mike Lockwood    private int[] getSupportedCaptureFormats() {
5994b322ce4fb86b5a7a6e50633a3a9f498ca8d4706Mike Lockwood        // no capture formats yet
6004b322ce4fb86b5a7a6e50633a3a9f498ca8d4706Mike Lockwood        return null;
6014b322ce4fb86b5a7a6e50633a3a9f498ca8d4706Mike Lockwood    }
6024b322ce4fb86b5a7a6e50633a3a9f498ca8d4706Mike Lockwood
603ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood    static final int[] FILE_PROPERTIES = {
604ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            // NOTE must match beginning of AUDIO_PROPERTIES, VIDEO_PROPERTIES
605ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            // and IMAGE_PROPERTIES below
6065367ab6aab2a61aaf534b18576dcdd0bca1b9681Mike Lockwood            MtpConstants.PROPERTY_STORAGE_ID,
6075367ab6aab2a61aaf534b18576dcdd0bca1b9681Mike Lockwood            MtpConstants.PROPERTY_OBJECT_FORMAT,
608d3bfecb0c9bdf3651a06e86c7c8607768a29a9aeMike Lockwood            MtpConstants.PROPERTY_PROTECTION_STATUS,
6095367ab6aab2a61aaf534b18576dcdd0bca1b9681Mike Lockwood            MtpConstants.PROPERTY_OBJECT_SIZE,
6105367ab6aab2a61aaf534b18576dcdd0bca1b9681Mike Lockwood            MtpConstants.PROPERTY_OBJECT_FILE_NAME,
611d3bfecb0c9bdf3651a06e86c7c8607768a29a9aeMike Lockwood            MtpConstants.PROPERTY_DATE_MODIFIED,
6125367ab6aab2a61aaf534b18576dcdd0bca1b9681Mike Lockwood            MtpConstants.PROPERTY_PARENT_OBJECT,
613d3bfecb0c9bdf3651a06e86c7c8607768a29a9aeMike Lockwood            MtpConstants.PROPERTY_PERSISTENT_UID,
614d3bfecb0c9bdf3651a06e86c7c8607768a29a9aeMike Lockwood            MtpConstants.PROPERTY_NAME,
61571827748105252791854b87da8e0e0c66ff0d2cbMike Lockwood            MtpConstants.PROPERTY_DISPLAY_NAME,
616ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_DATE_ADDED,
617ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood    };
618ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood
619ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood    static final int[] AUDIO_PROPERTIES = {
620ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            // NOTE must match FILE_PROPERTIES above
621ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_STORAGE_ID,
622ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_OBJECT_FORMAT,
623ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_PROTECTION_STATUS,
624ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_OBJECT_SIZE,
625ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_OBJECT_FILE_NAME,
626ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_DATE_MODIFIED,
627ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_PARENT_OBJECT,
628ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_PERSISTENT_UID,
629ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_NAME,
630ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_DISPLAY_NAME,
631ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_DATE_ADDED,
632ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood
633ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            // audio specific properties
634ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_ARTIST,
635ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_ALBUM_NAME,
636ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_ALBUM_ARTIST,
637ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_TRACK,
638ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_ORIGINAL_RELEASE_DATE,
639ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_DURATION,
640ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_GENRE,
641ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_COMPOSER,
64292b53bc2dd42a3c1d2d7708145099da2a5fe541eMike Lockwood            MtpConstants.PROPERTY_AUDIO_WAVE_CODEC,
64392b53bc2dd42a3c1d2d7708145099da2a5fe541eMike Lockwood            MtpConstants.PROPERTY_BITRATE_TYPE,
64492b53bc2dd42a3c1d2d7708145099da2a5fe541eMike Lockwood            MtpConstants.PROPERTY_AUDIO_BITRATE,
64592b53bc2dd42a3c1d2d7708145099da2a5fe541eMike Lockwood            MtpConstants.PROPERTY_NUMBER_OF_CHANNELS,
64692b53bc2dd42a3c1d2d7708145099da2a5fe541eMike Lockwood            MtpConstants.PROPERTY_SAMPLE_RATE,
647ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood    };
648ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood
649ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood    static final int[] VIDEO_PROPERTIES = {
650ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            // NOTE must match FILE_PROPERTIES above
651ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_STORAGE_ID,
652ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_OBJECT_FORMAT,
653ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_PROTECTION_STATUS,
654ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_OBJECT_SIZE,
655ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_OBJECT_FILE_NAME,
656ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_DATE_MODIFIED,
657ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_PARENT_OBJECT,
658ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_PERSISTENT_UID,
659ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_NAME,
660ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_DISPLAY_NAME,
661ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_DATE_ADDED,
662ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood
663ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            // video specific properties
664ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_ARTIST,
665ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_ALBUM_NAME,
666ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_DURATION,
667ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_DESCRIPTION,
668ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood    };
669ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood
670ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood    static final int[] IMAGE_PROPERTIES = {
671ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            // NOTE must match FILE_PROPERTIES above
672ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_STORAGE_ID,
673ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_OBJECT_FORMAT,
674ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_PROTECTION_STATUS,
675ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_OBJECT_SIZE,
676ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_OBJECT_FILE_NAME,
677ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_DATE_MODIFIED,
678ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_PARENT_OBJECT,
679ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_PERSISTENT_UID,
680ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_NAME,
681ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_DISPLAY_NAME,
682ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_DATE_ADDED,
683ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood
684ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            // image specific properties
685ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            MtpConstants.PROPERTY_DESCRIPTION,
686ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood    };
687ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood
688ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood    private int[] getSupportedObjectProperties(int format) {
689ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood        switch (format) {
690ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            case MtpConstants.FORMAT_MP3:
691ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            case MtpConstants.FORMAT_WAV:
692ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            case MtpConstants.FORMAT_WMA:
693ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            case MtpConstants.FORMAT_OGG:
694ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            case MtpConstants.FORMAT_AAC:
695ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood                return AUDIO_PROPERTIES;
696ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            case MtpConstants.FORMAT_MPEG:
697ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            case MtpConstants.FORMAT_3GP_CONTAINER:
698ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            case MtpConstants.FORMAT_WMV:
699ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood                return VIDEO_PROPERTIES;
700ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            case MtpConstants.FORMAT_EXIF_JPEG:
701ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            case MtpConstants.FORMAT_GIF:
702ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            case MtpConstants.FORMAT_PNG:
703ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            case MtpConstants.FORMAT_BMP:
7045a8b9627c7fdacc94791fb3024386739f8cf9a56Jaesung Chung            case MtpConstants.FORMAT_DNG:
705ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood                return IMAGE_PROPERTIES;
706ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood            default:
707ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood                return FILE_PROPERTIES;
708ae078f7dacdc719d045c2d19bbce019599fec64eMike Lockwood        }
7094b322ce4fb86b5a7a6e50633a3a9f498ca8d4706Mike Lockwood    }
7104b322ce4fb86b5a7a6e50633a3a9f498ca8d4706Mike Lockwood
7114b322ce4fb86b5a7a6e50633a3a9f498ca8d4706Mike Lockwood    private int[] getSupportedDeviceProperties() {
71259e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood        return new int[] {
71359e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood            MtpConstants.DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER,
71459e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood            MtpConstants.DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME,
715ea93fa16b166ff477e9cf6bd3ac67c8fb259fb57Mike Lockwood            MtpConstants.DEVICE_PROPERTY_IMAGE_SIZE,
71656c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood            MtpConstants.DEVICE_PROPERTY_BATTERY_LEVEL,
71713bb2f4ddadcfee8e82a0eea3cb76a2448bb2ccbJerry Zhang            MtpConstants.DEVICE_PROPERTY_PERCEIVED_DEVICE_TYPE,
71859e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood        };
7194b322ce4fb86b5a7a6e50633a3a9f498ca8d4706Mike Lockwood    }
7204b322ce4fb86b5a7a6e50633a3a9f498ca8d4706Mike Lockwood
721486ad2e73c1c24c0585d414b18bd623c54ac1547Daichi Hirono    private MtpPropertyList getObjectPropertyList(int handle, int format, int property,
722e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood                        int groupCode, int depth) {
723e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood        // FIXME - implement group support
724e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood        if (groupCode != 0) {
725e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood            return new MtpPropertyList(0, MtpConstants.RESPONSE_SPECIFICATION_BY_GROUP_UNSUPPORTED);
7265ebac83e20dadf10bd4a0cee4ddf69d0ec049f98Mike Lockwood        }
7275ebac83e20dadf10bd4a0cee4ddf69d0ec049f98Mike Lockwood
7287d7fb63071ca370f77ed69055ac30341b8388d15Mike Lockwood        MtpPropertyGroup propertyGroup;
729486ad2e73c1c24c0585d414b18bd623c54ac1547Daichi Hirono        if (property == 0xffffffff) {
730486ad2e73c1c24c0585d414b18bd623c54ac1547Daichi Hirono            if (format == 0 && handle != 0 && handle != 0xffffffff) {
73171827748105252791854b87da8e0e0c66ff0d2cbMike Lockwood                // return properties based on the object's format
732486ad2e73c1c24c0585d414b18bd623c54ac1547Daichi Hirono                format = getObjectFormat(handle);
73371827748105252791854b87da8e0e0c66ff0d2cbMike Lockwood            }
734486ad2e73c1c24c0585d414b18bd623c54ac1547Daichi Hirono            propertyGroup = mPropertyGroupsByFormat.get(format);
735486ad2e73c1c24c0585d414b18bd623c54ac1547Daichi Hirono            if (propertyGroup == null) {
7367d7fb63071ca370f77ed69055ac30341b8388d15Mike Lockwood                int[] propertyList = getSupportedObjectProperties(format);
73760cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey                propertyGroup = new MtpPropertyGroup(this, mMediaProvider,
73835654b61e8fe7bc85afcb076ddbb590d51c5865fDianne Hackborn                        mVolumeName, propertyList);
739486ad2e73c1c24c0585d414b18bd623c54ac1547Daichi Hirono                mPropertyGroupsByFormat.put(format, propertyGroup);
740e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood            }
7417d7fb63071ca370f77ed69055ac30341b8388d15Mike Lockwood        } else {
742486ad2e73c1c24c0585d414b18bd623c54ac1547Daichi Hirono            propertyGroup = mPropertyGroupsByProperty.get(property);
743486ad2e73c1c24c0585d414b18bd623c54ac1547Daichi Hirono            if (propertyGroup == null) {
744486ad2e73c1c24c0585d414b18bd623c54ac1547Daichi Hirono                final int[] propertyList = new int[] { property };
745486ad2e73c1c24c0585d414b18bd623c54ac1547Daichi Hirono                propertyGroup = new MtpPropertyGroup(
746486ad2e73c1c24c0585d414b18bd623c54ac1547Daichi Hirono                        this, mMediaProvider, mVolumeName, propertyList);
747486ad2e73c1c24c0585d414b18bd623c54ac1547Daichi Hirono                mPropertyGroupsByProperty.put(property, propertyGroup);
748e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood            }
749e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood        }
7507d7fb63071ca370f77ed69055ac30341b8388d15Mike Lockwood
751486ad2e73c1c24c0585d414b18bd623c54ac1547Daichi Hirono        return propertyGroup.getPropertyList(handle, format, depth);
752e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood    }
753e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood
754e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood    private int renameFile(int handle, String newName) {
755e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood        Cursor c = null;
756e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood
757e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood        // first compute current path
758e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood        String path = null;
759e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood        String[] whereArgs = new String[] {  Integer.toString(handle) };
760e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood        try {
76160cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey            c = mMediaProvider.query(mObjectsUri, PATH_PROJECTION, ID_WHERE,
76235654b61e8fe7bc85afcb076ddbb590d51c5865fDianne Hackborn                    whereArgs, null, null);
763e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood            if (c != null && c.moveToNext()) {
7641c4e88d0b0a8deede0415183739478b5ebc12bfdMike Lockwood                path = c.getString(1);
765e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood            }
766e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood        } catch (RemoteException e) {
767e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood            Log.e(TAG, "RemoteException in getObjectFilePath", e);
768e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood            return MtpConstants.RESPONSE_GENERAL_ERROR;
769e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood        } finally {
770e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood            if (c != null) {
771e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood                c.close();
772b2cada9034436b4ea4bac218da7a1e29460d15b2Mike Lockwood            }
773b2cada9034436b4ea4bac218da7a1e29460d15b2Mike Lockwood        }
774e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood        if (path == null) {
775e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood            return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
776e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood        }
777e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood
77873e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood        // do not allow renaming any of the special subdirectories
77973e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood        if (isStorageSubDirectory(path)) {
78073e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood            return MtpConstants.RESPONSE_OBJECT_WRITE_PROTECTED;
78173e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood        }
78273e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood
783e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood        // now rename the file.  make sure this succeeds before updating database
784e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood        File oldFile = new File(path);
785e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood        int lastSlash = path.lastIndexOf('/');
786e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood        if (lastSlash <= 1) {
787e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood            return MtpConstants.RESPONSE_GENERAL_ERROR;
788e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood        }
789e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood        String newPath = path.substring(0, lastSlash + 1) + newName;
790e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood        File newFile = new File(newPath);
791e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood        boolean success = oldFile.renameTo(newFile);
792e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood        if (!success) {
793f26a586c86b097f975e26fe526ead564ad011bd0Mike Lockwood            Log.w(TAG, "renaming "+ path + " to " + newPath + " failed");
794e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood            return MtpConstants.RESPONSE_GENERAL_ERROR;
795e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood        }
796e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood
797e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood        // finally update database
798e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood        ContentValues values = new ContentValues();
799e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood        values.put(Files.FileColumns.DATA, newPath);
800e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood        int updated = 0;
801e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood        try {
802e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood            // note - we are relying on a special case in MediaProvider.update() to update
803e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood            // the paths for all children in the case where this is a directory.
80460cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey            updated = mMediaProvider.update(mObjectsUri, values, ID_WHERE, whereArgs);
805e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood        } catch (RemoteException e) {
806e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood            Log.e(TAG, "RemoteException in mMediaProvider.update", e);
807e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood        }
808e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood        if (updated == 0) {
809e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood            Log.e(TAG, "Unable to update path for " + path + " to " + newPath);
810e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood            // this shouldn't happen, but if it does we need to rename the file to its original name
811e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood            newFile.renameTo(oldFile);
812e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood            return MtpConstants.RESPONSE_GENERAL_ERROR;
813e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood        }
814e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood
815ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen        // check if nomedia status changed
816ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen        if (newFile.isDirectory()) {
817ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen            // for directories, check if renamed from something hidden to something non-hidden
818ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen            if (oldFile.getName().startsWith(".") && !newPath.startsWith(".")) {
819ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                // directory was unhidden
820ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                try {
82160cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey                    mMediaProvider.call(MediaStore.UNHIDE_CALL, newPath, null);
822ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                } catch (RemoteException e) {
823ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                    Log.e(TAG, "failed to unhide/rescan for " + newPath);
824ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                }
825ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen            }
826ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen        } else {
827ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen            // for files, check if renamed from .nomedia to something else
828ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen            if (oldFile.getName().toLowerCase(Locale.US).equals(".nomedia")
829ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                    && !newPath.toLowerCase(Locale.US).equals(".nomedia")) {
830ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                try {
83160cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey                    mMediaProvider.call(MediaStore.UNHIDE_CALL, oldFile.getParent(), null);
832ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                } catch (RemoteException e) {
833ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                    Log.e(TAG, "failed to unhide/rescan for " + newPath);
834ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                }
835ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen            }
836ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen        }
837ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen
838e2ad6ec1718ef0c0e8230f8f62e7cfefcf598b6aMike Lockwood        return MtpConstants.RESPONSE_OK;
839d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood    }
840d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood
84159e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood    private int setObjectProperty(int handle, int property,
84259e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood                            long intValue, String stringValue) {
8435ebac83e20dadf10bd4a0cee4ddf69d0ec049f98Mike Lockwood        switch (property) {
8445ebac83e20dadf10bd4a0cee4ddf69d0ec049f98Mike Lockwood            case MtpConstants.PROPERTY_OBJECT_FILE_NAME:
8455ebac83e20dadf10bd4a0cee4ddf69d0ec049f98Mike Lockwood                return renameFile(handle, stringValue);
8465ebac83e20dadf10bd4a0cee4ddf69d0ec049f98Mike Lockwood
8475ebac83e20dadf10bd4a0cee4ddf69d0ec049f98Mike Lockwood            default:
8485ebac83e20dadf10bd4a0cee4ddf69d0ec049f98Mike Lockwood                return MtpConstants.RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
8495ebac83e20dadf10bd4a0cee4ddf69d0ec049f98Mike Lockwood        }
85059e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood    }
85159e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood
85259e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood    private int getDeviceProperty(int property, long[] outIntValue, char[] outStringValue) {
85359e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood        switch (property) {
85459e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood            case MtpConstants.DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER:
85559e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood            case MtpConstants.DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME:
856775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood                // writable string properties kept in shared preferences
857775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood                String value = mDeviceProperties.getString(Integer.toString(property), "");
858775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood                int length = value.length();
859775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood                if (length > 255) {
860775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood                    length = 255;
86159e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood                }
862775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood                value.getChars(0, length, outStringValue, 0);
863775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood                outStringValue[length] = 0;
864775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood                return MtpConstants.RESPONSE_OK;
86559e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood
866ea93fa16b166ff477e9cf6bd3ac67c8fb259fb57Mike Lockwood            case MtpConstants.DEVICE_PROPERTY_IMAGE_SIZE:
867ea93fa16b166ff477e9cf6bd3ac67c8fb259fb57Mike Lockwood                // use screen size as max image size
868ea93fa16b166ff477e9cf6bd3ac67c8fb259fb57Mike Lockwood                Display display = ((WindowManager)mContext.getSystemService(
869ea93fa16b166ff477e9cf6bd3ac67c8fb259fb57Mike Lockwood                        Context.WINDOW_SERVICE)).getDefaultDisplay();
87044bc17c6b517aef35a390c81b5aa79c4f284f744Dianne Hackborn                int width = display.getMaximumSizeDimension();
87144bc17c6b517aef35a390c81b5aa79c4f284f744Dianne Hackborn                int height = display.getMaximumSizeDimension();
872ea93fa16b166ff477e9cf6bd3ac67c8fb259fb57Mike Lockwood                String imageSize = Integer.toString(width) + "x" +  Integer.toString(height);
873ea93fa16b166ff477e9cf6bd3ac67c8fb259fb57Mike Lockwood                imageSize.getChars(0, imageSize.length(), outStringValue, 0);
874ea93fa16b166ff477e9cf6bd3ac67c8fb259fb57Mike Lockwood                outStringValue[imageSize.length()] = 0;
875ea93fa16b166ff477e9cf6bd3ac67c8fb259fb57Mike Lockwood                return MtpConstants.RESPONSE_OK;
876ea93fa16b166ff477e9cf6bd3ac67c8fb259fb57Mike Lockwood
87713bb2f4ddadcfee8e82a0eea3cb76a2448bb2ccbJerry Zhang            case MtpConstants.DEVICE_PROPERTY_PERCEIVED_DEVICE_TYPE:
87813bb2f4ddadcfee8e82a0eea3cb76a2448bb2ccbJerry Zhang                outIntValue[0] = mDeviceType;
87913bb2f4ddadcfee8e82a0eea3cb76a2448bb2ccbJerry Zhang                return MtpConstants.RESPONSE_OK;
88013bb2f4ddadcfee8e82a0eea3cb76a2448bb2ccbJerry Zhang
88156c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood            // DEVICE_PROPERTY_BATTERY_LEVEL is implemented in the JNI code
88256c85244b9be0fc6f6c2baf5f9a53f2364d5ec5eMike Lockwood
883ea93fa16b166ff477e9cf6bd3ac67c8fb259fb57Mike Lockwood            default:
884ea93fa16b166ff477e9cf6bd3ac67c8fb259fb57Mike Lockwood                return MtpConstants.RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
885ea93fa16b166ff477e9cf6bd3ac67c8fb259fb57Mike Lockwood        }
88659e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood    }
88759e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood
88859e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood    private int setDeviceProperty(int property, long intValue, String stringValue) {
88959e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood        switch (property) {
89059e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood            case MtpConstants.DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER:
89159e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood            case MtpConstants.DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME:
892775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood                // writable string properties kept in shared prefs
893775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood                SharedPreferences.Editor e = mDeviceProperties.edit();
894775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood                e.putString(Integer.toString(property), stringValue);
895775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood                return (e.commit() ? MtpConstants.RESPONSE_OK
896775de951a8d4f4fe58c849efd9ef7b92c4ade907Mike Lockwood                        : MtpConstants.RESPONSE_GENERAL_ERROR);
89759e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood        }
89859e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood
89959e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood        return MtpConstants.RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
90059e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood    }
90159e3f0db0855567a95f783f6d1ec36a7cfc4ea83Mike Lockwood
902d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood    private boolean getObjectInfo(int handle, int[] outStorageFormatParent,
9031341f1eee3493fc5e263494acb8adebb6f0180b6Mike Lockwood                        char[] outName, long[] outCreatedModified) {
904d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood        Cursor c = null;
905d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood        try {
90660cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey            c = mMediaProvider.query(mObjectsUri, OBJECT_INFO_PROJECTION,
90775ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                            ID_WHERE, new String[] {  Integer.toString(handle) }, null, null);
908d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood            if (c != null && c.moveToNext()) {
909b239b683765f63d79e74b8ba2a8cc2855f236536Mike Lockwood                outStorageFormatParent[0] = c.getInt(1);
910d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood                outStorageFormatParent[1] = c.getInt(2);
911d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood                outStorageFormatParent[2] = c.getInt(3);
912d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood
913d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood                // extract name from path
914b239b683765f63d79e74b8ba2a8cc2855f236536Mike Lockwood                String path = c.getString(4);
915d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood                int lastSlash = path.lastIndexOf('/');
916d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood                int start = (lastSlash >= 0 ? lastSlash + 1 : 0);
917d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood                int end = path.length();
918d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood                if (end - start > 255) {
919d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood                    end = start + 255;
920d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood                }
921d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood                path.getChars(start, end, outName, 0);
922d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood                outName[end - start] = 0;
923d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood
9241341f1eee3493fc5e263494acb8adebb6f0180b6Mike Lockwood                outCreatedModified[0] = c.getLong(5);
9251341f1eee3493fc5e263494acb8adebb6f0180b6Mike Lockwood                outCreatedModified[1] = c.getLong(6);
9261341f1eee3493fc5e263494acb8adebb6f0180b6Mike Lockwood                // use modification date as creation date if date added is not set
9271341f1eee3493fc5e263494acb8adebb6f0180b6Mike Lockwood                if (outCreatedModified[0] == 0) {
9281341f1eee3493fc5e263494acb8adebb6f0180b6Mike Lockwood                    outCreatedModified[0] = outCreatedModified[1];
9291341f1eee3493fc5e263494acb8adebb6f0180b6Mike Lockwood                }
930d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood                return true;
931d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood            }
932d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood        } catch (RemoteException e) {
9332b5f9ad1eaf0d6daaca5cf3761434a09309902bbMike Lockwood            Log.e(TAG, "RemoteException in getObjectInfo", e);
934d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood        } finally {
935d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood            if (c != null) {
936d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood                c.close();
937d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood            }
938d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood        }
939d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood        return false;
940d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood    }
941d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood
942365e03e4943b2d2f4d1e602409ab0e770ea77d1dMike Lockwood    private int getObjectFilePath(int handle, char[] outFilePath, long[] outFileLengthFormat) {
94301788562f26f213f63c14483fd3977e23b8314abMike Lockwood        if (handle == 0) {
94401788562f26f213f63c14483fd3977e23b8314abMike Lockwood            // special case root directory
94501788562f26f213f63c14483fd3977e23b8314abMike Lockwood            mMediaStoragePath.getChars(0, mMediaStoragePath.length(), outFilePath, 0);
94601788562f26f213f63c14483fd3977e23b8314abMike Lockwood            outFilePath[mMediaStoragePath.length()] = 0;
947365e03e4943b2d2f4d1e602409ab0e770ea77d1dMike Lockwood            outFileLengthFormat[0] = 0;
948365e03e4943b2d2f4d1e602409ab0e770ea77d1dMike Lockwood            outFileLengthFormat[1] = MtpConstants.FORMAT_ASSOCIATION;
94901788562f26f213f63c14483fd3977e23b8314abMike Lockwood            return MtpConstants.RESPONSE_OK;
95001788562f26f213f63c14483fd3977e23b8314abMike Lockwood        }
951d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood        Cursor c = null;
952d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood        try {
95360cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey            c = mMediaProvider.query(mObjectsUri, PATH_FORMAT_PROJECTION,
95475ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                            ID_WHERE, new String[] {  Integer.toString(handle) }, null, null);
955d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood            if (c != null && c.moveToNext()) {
9561c4e88d0b0a8deede0415183739478b5ebc12bfdMike Lockwood                String path = c.getString(1);
957d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood                path.getChars(0, path.length(), outFilePath, 0);
958d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood                outFilePath[path.length()] = 0;
959f6f16614574767263fcd0073f72c099edaca0607Mike Lockwood                // File transfers from device to host will likely fail if the size is incorrect.
960f6f16614574767263fcd0073f72c099edaca0607Mike Lockwood                // So to be safe, use the actual file size here.
961f6f16614574767263fcd0073f72c099edaca0607Mike Lockwood                outFileLengthFormat[0] = new File(path).length();
962f6f16614574767263fcd0073f72c099edaca0607Mike Lockwood                outFileLengthFormat[1] = c.getLong(2);
9635367ab6aab2a61aaf534b18576dcdd0bca1b9681Mike Lockwood                return MtpConstants.RESPONSE_OK;
96459c777a24a2c639dfd84b7bf17e430e6d3424866Mike Lockwood            } else {
9655367ab6aab2a61aaf534b18576dcdd0bca1b9681Mike Lockwood                return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
966d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood            }
967d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood        } catch (RemoteException e) {
968d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood            Log.e(TAG, "RemoteException in getObjectFilePath", e);
9695367ab6aab2a61aaf534b18576dcdd0bca1b9681Mike Lockwood            return MtpConstants.RESPONSE_GENERAL_ERROR;
970d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood        } finally {
971d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood            if (c != null) {
972d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood                c.close();
973d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood            }
974d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood        }
975d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood    }
976d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood
97771827748105252791854b87da8e0e0c66ff0d2cbMike Lockwood    private int getObjectFormat(int handle) {
97871827748105252791854b87da8e0e0c66ff0d2cbMike Lockwood        Cursor c = null;
97971827748105252791854b87da8e0e0c66ff0d2cbMike Lockwood        try {
98060cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey            c = mMediaProvider.query(mObjectsUri, FORMAT_PROJECTION,
981486ad2e73c1c24c0585d414b18bd623c54ac1547Daichi Hirono                            ID_WHERE, new String[] { Integer.toString(handle) }, null, null);
98271827748105252791854b87da8e0e0c66ff0d2cbMike Lockwood            if (c != null && c.moveToNext()) {
98371827748105252791854b87da8e0e0c66ff0d2cbMike Lockwood                return c.getInt(1);
98471827748105252791854b87da8e0e0c66ff0d2cbMike Lockwood            } else {
98571827748105252791854b87da8e0e0c66ff0d2cbMike Lockwood                return -1;
98671827748105252791854b87da8e0e0c66ff0d2cbMike Lockwood            }
98771827748105252791854b87da8e0e0c66ff0d2cbMike Lockwood        } catch (RemoteException e) {
98871827748105252791854b87da8e0e0c66ff0d2cbMike Lockwood            Log.e(TAG, "RemoteException in getObjectFilePath", e);
98971827748105252791854b87da8e0e0c66ff0d2cbMike Lockwood            return -1;
99071827748105252791854b87da8e0e0c66ff0d2cbMike Lockwood        } finally {
99171827748105252791854b87da8e0e0c66ff0d2cbMike Lockwood            if (c != null) {
99271827748105252791854b87da8e0e0c66ff0d2cbMike Lockwood                c.close();
99371827748105252791854b87da8e0e0c66ff0d2cbMike Lockwood            }
99471827748105252791854b87da8e0e0c66ff0d2cbMike Lockwood        }
99571827748105252791854b87da8e0e0c66ff0d2cbMike Lockwood    }
99671827748105252791854b87da8e0e0c66ff0d2cbMike Lockwood
99759c777a24a2c639dfd84b7bf17e430e6d3424866Mike Lockwood    private int deleteFile(int handle) {
9982837eefc5459427138c080d445bb491c75630163Mike Lockwood        mDatabaseModified = true;
99955f808cff9913a00164120a422af6ca45db5072bMike Lockwood        String path = null;
100055f808cff9913a00164120a422af6ca45db5072bMike Lockwood        int format = 0;
100155f808cff9913a00164120a422af6ca45db5072bMike Lockwood
100255f808cff9913a00164120a422af6ca45db5072bMike Lockwood        Cursor c = null;
1003d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood        try {
100460cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey            c = mMediaProvider.query(mObjectsUri, PATH_FORMAT_PROJECTION,
100575ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                            ID_WHERE, new String[] {  Integer.toString(handle) }, null, null);
100655f808cff9913a00164120a422af6ca45db5072bMike Lockwood            if (c != null && c.moveToNext()) {
100755f808cff9913a00164120a422af6ca45db5072bMike Lockwood                // don't convert to media path here, since we will be matching
100855f808cff9913a00164120a422af6ca45db5072bMike Lockwood                // against paths in the database matching /data/media
100955f808cff9913a00164120a422af6ca45db5072bMike Lockwood                path = c.getString(1);
1010f6f16614574767263fcd0073f72c099edaca0607Mike Lockwood                format = c.getInt(2);
101155f808cff9913a00164120a422af6ca45db5072bMike Lockwood            } else {
101255f808cff9913a00164120a422af6ca45db5072bMike Lockwood                return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
101355f808cff9913a00164120a422af6ca45db5072bMike Lockwood            }
101455f808cff9913a00164120a422af6ca45db5072bMike Lockwood
101555f808cff9913a00164120a422af6ca45db5072bMike Lockwood            if (path == null || format == 0) {
101655f808cff9913a00164120a422af6ca45db5072bMike Lockwood                return MtpConstants.RESPONSE_GENERAL_ERROR;
101755f808cff9913a00164120a422af6ca45db5072bMike Lockwood            }
101855f808cff9913a00164120a422af6ca45db5072bMike Lockwood
101973e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood            // do not allow deleting any of the special subdirectories
102073e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood            if (isStorageSubDirectory(path)) {
102173e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood                return MtpConstants.RESPONSE_OBJECT_WRITE_PROTECTED;
102273e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood            }
102373e56d935e7c9b8a06e4781c4995e407e118f93dMike Lockwood
102455f808cff9913a00164120a422af6ca45db5072bMike Lockwood            if (format == MtpConstants.FORMAT_ASSOCIATION) {
102555f808cff9913a00164120a422af6ca45db5072bMike Lockwood                // recursive case - delete all children first
102655f808cff9913a00164120a422af6ca45db5072bMike Lockwood                Uri uri = Files.getMtpObjectsUri(mVolumeName);
102760cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey                int count = mMediaProvider.delete(uri,
10281e855d98aa979b30cee9e096cf6a1cb54df0367bMike Lockwood                    // the 'like' makes it use the index, the 'lower()' makes it correct
10291e855d98aa979b30cee9e096cf6a1cb54df0367bMike Lockwood                    // when the path contains sqlite wildcard characters
10301e855d98aa979b30cee9e096cf6a1cb54df0367bMike Lockwood                    "_data LIKE ?1 AND lower(substr(_data,1,?2))=lower(?3)",
10311e855d98aa979b30cee9e096cf6a1cb54df0367bMike Lockwood                    new String[] { path + "/%",Integer.toString(path.length() + 1), path + "/"});
103255f808cff9913a00164120a422af6ca45db5072bMike Lockwood            }
103355f808cff9913a00164120a422af6ca45db5072bMike Lockwood
103455f808cff9913a00164120a422af6ca45db5072bMike Lockwood            Uri uri = Files.getMtpObjectsUri(mVolumeName, handle);
103560cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey            if (mMediaProvider.delete(uri, null, null) > 0) {
1036ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                if (format != MtpConstants.FORMAT_ASSOCIATION
1037ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                        && path.toLowerCase(Locale.US).endsWith("/.nomedia")) {
1038ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                    try {
1039ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                        String parentPath = path.substring(0, path.lastIndexOf("/"));
104060cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey                        mMediaProvider.call(MediaStore.UNHIDE_CALL, parentPath, null);
1041ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                    } catch (RemoteException e) {
1042ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                        Log.e(TAG, "failed to unhide/rescan for " + path);
1043ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                    }
1044ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                }
10455367ab6aab2a61aaf534b18576dcdd0bca1b9681Mike Lockwood                return MtpConstants.RESPONSE_OK;
104659c777a24a2c639dfd84b7bf17e430e6d3424866Mike Lockwood            } else {
10475367ab6aab2a61aaf534b18576dcdd0bca1b9681Mike Lockwood                return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
104859c777a24a2c639dfd84b7bf17e430e6d3424866Mike Lockwood            }
1049d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood        } catch (RemoteException e) {
1050d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood            Log.e(TAG, "RemoteException in deleteFile", e);
10515367ab6aab2a61aaf534b18576dcdd0bca1b9681Mike Lockwood            return MtpConstants.RESPONSE_GENERAL_ERROR;
105255f808cff9913a00164120a422af6ca45db5072bMike Lockwood        } finally {
105355f808cff9913a00164120a422af6ca45db5072bMike Lockwood            if (c != null) {
105455f808cff9913a00164120a422af6ca45db5072bMike Lockwood                c.close();
105555f808cff9913a00164120a422af6ca45db5072bMike Lockwood            }
1056d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood        }
1057d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood    }
1058d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood
10599a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood    private int[] getObjectReferences(int handle) {
10608490e66f57506d4e4b05e7c987c7ca34295843e6Mike Lockwood        Uri uri = Files.getMtpReferencesUri(mVolumeName, handle);
10619a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood        Cursor c = null;
10629a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood        try {
106360cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey            c = mMediaProvider.query(uri, ID_PROJECTION, null, null, null, null);
10649a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood            if (c == null) {
10659a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood                return null;
10669a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood            }
10679a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood            int count = c.getCount();
10689a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood            if (count > 0) {
10699a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood                int[] result = new int[count];
10709a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood                for (int i = 0; i < count; i++) {
10719a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood                    c.moveToNext();
10729a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood                    result[i] = c.getInt(0);
10739a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood                }
10749a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood                return result;
10759a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood            }
10769a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood        } catch (RemoteException e) {
10779a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood            Log.e(TAG, "RemoteException in getObjectList", e);
10789a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood        } finally {
10799a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood            if (c != null) {
10809a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood                c.close();
10819a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood            }
10829a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood        }
10839a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood        return null;
10849a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood    }
10859a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood
10869a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood    private int setObjectReferences(int handle, int[] references) {
10872837eefc5459427138c080d445bb491c75630163Mike Lockwood        mDatabaseModified = true;
10888490e66f57506d4e4b05e7c987c7ca34295843e6Mike Lockwood        Uri uri = Files.getMtpReferencesUri(mVolumeName, handle);
10899a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood        int count = references.length;
10909a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood        ContentValues[] valuesList = new ContentValues[count];
10919a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood        for (int i = 0; i < count; i++) {
10929a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood            ContentValues values = new ContentValues();
10933b2a62e5b35ea5f8da9d9bee52f75c9c2ebb4eceMike Lockwood            values.put(Files.FileColumns._ID, references[i]);
10949a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood            valuesList[i] = values;
10959a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood        }
10969a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood        try {
109760cfad80bdf61db436643927337c2fb30186e99dJeff Sharkey            if (mMediaProvider.bulkInsert(uri, valuesList) > 0) {
10985367ab6aab2a61aaf534b18576dcdd0bca1b9681Mike Lockwood                return MtpConstants.RESPONSE_OK;
10999a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood            }
11009a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood        } catch (RemoteException e) {
11019a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood            Log.e(TAG, "RemoteException in setObjectReferences", e);
11029a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood        }
11035367ab6aab2a61aaf534b18576dcdd0bca1b9681Mike Lockwood        return MtpConstants.RESPONSE_GENERAL_ERROR;
11049a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood    }
11059a2046fb5ceeee4d5334274cbff15f1058bb3244Mike Lockwood
11062837eefc5459427138c080d445bb491c75630163Mike Lockwood    private void sessionStarted() {
11072837eefc5459427138c080d445bb491c75630163Mike Lockwood        mDatabaseModified = false;
11082837eefc5459427138c080d445bb491c75630163Mike Lockwood    }
11092837eefc5459427138c080d445bb491c75630163Mike Lockwood
11102837eefc5459427138c080d445bb491c75630163Mike Lockwood    private void sessionEnded() {
11112837eefc5459427138c080d445bb491c75630163Mike Lockwood        if (mDatabaseModified) {
1112a31560598af25d5116effc2a6af0d9be12a0628dMike Lockwood            mContext.sendBroadcast(new Intent(MediaStore.ACTION_MTP_SESSION_END));
11132837eefc5459427138c080d445bb491c75630163Mike Lockwood            mDatabaseModified = false;
11142837eefc5459427138c080d445bb491c75630163Mike Lockwood        }
11152837eefc5459427138c080d445bb491c75630163Mike Lockwood    }
11162837eefc5459427138c080d445bb491c75630163Mike Lockwood
1117d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood    // used by the JNI code
1118e2e59326f13cf291bf5b37989390fd2a55a94f0cAshok Bhat    private long mNativeContext;
1119d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood
1120d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood    private native final void native_setup();
1121d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood    private native final void native_finalize();
1122d21eac9c70940f2c73da5faaf401dbbc44b70a15Mike Lockwood}
1123