16baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono/* 26baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono * Copyright (C) 2015 The Android Open Source Project 36baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono * 46baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono * Licensed under the Apache License, Version 2.0 (the "License"); 56baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono * you may not use this file except in compliance with the License. 66baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono * You may obtain a copy of the License at 76baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono * 86baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono * http://www.apache.org/licenses/LICENSE-2.0 96baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono * 106baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono * Unless required by applicable law or agreed to in writing, software 116baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono * distributed under the License is distributed on an "AS IS" BASIS, 126baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 136baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono * See the License for the specific language governing permissions and 146baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono * limitations under the License. 156baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono */ 166baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono 176baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hironopackage com.android.mtp; 186baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono 194e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hironoimport android.annotation.Nullable; 204e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hironoimport android.annotation.WorkerThread; 216baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hironoimport android.content.ContentResolver; 226baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hironoimport android.database.Cursor; 2364111e08d905525c7f4fe27e69953eb71bd62511Daichi Hironoimport android.mtp.MtpConstants; 24bb430fa930fa0d0700e46e7b4881de2a252223ddTomasz Mikolajewskiimport android.mtp.MtpObjectInfo; 256baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hironoimport android.net.Uri; 266baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hironoimport android.os.Bundle; 276baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hironoimport android.os.Process; 286baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hironoimport android.provider.DocumentsContract; 29cfaab20c24ab1790a6aa353d9a1005b5850a7144Daichi Hironoimport android.util.Log; 306baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono 314e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hironoimport com.android.internal.util.Preconditions; 324e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono 3347eb192b2704e27272ca94a95680cac40b6bff3fDaichi Hironoimport java.io.FileNotFoundException; 346baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hironoimport java.io.IOException; 35cfaab20c24ab1790a6aa353d9a1005b5850a7144Daichi Hironoimport java.util.ArrayList; 366baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hironoimport java.util.Date; 376baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hironoimport java.util.LinkedList; 386baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono 39d40b030ad55dfe8cb408fa35e0fb94400e2dcbb8Daichi Hirono/** 40d40b030ad55dfe8cb408fa35e0fb94400e2dcbb8Daichi Hirono * Loader for MTP document. 41d40b030ad55dfe8cb408fa35e0fb94400e2dcbb8Daichi Hirono * At the first request, the loader returns only first NUM_INITIAL_ENTRIES. Then it launches 42d40b030ad55dfe8cb408fa35e0fb94400e2dcbb8Daichi Hirono * background thread to load the rest documents and caches its result for next requests. 43bb430fa930fa0d0700e46e7b4881de2a252223ddTomasz Mikolajewski * TODO: Rename this class to ObjectInfoLoader 44d40b030ad55dfe8cb408fa35e0fb94400e2dcbb8Daichi Hirono */ 454e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hironoclass DocumentLoader implements AutoCloseable { 466baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono static final int NUM_INITIAL_ENTRIES = 10; 476baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono static final int NUM_LOADING_ENTRIES = 20; 486baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono static final int NOTIFY_PERIOD_MS = 500; 496baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono 5061ba923ca0cb5c928a16729d0aa67b6bf4b2f027Daichi Hirono private final MtpDeviceRecord mDevice; 516baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono private final MtpManager mMtpManager; 526baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono private final ContentResolver mResolver; 5347eb192b2704e27272ca94a95680cac40b6bff3fDaichi Hirono private final MtpDatabase mDatabase; 54d40b030ad55dfe8cb408fa35e0fb94400e2dcbb8Daichi Hirono private final TaskList mTaskList = new TaskList(); 554e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono private Thread mBackgroundThread; 566baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono 5761ba923ca0cb5c928a16729d0aa67b6bf4b2f027Daichi Hirono DocumentLoader(MtpDeviceRecord device, MtpManager mtpManager, ContentResolver resolver, 584e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono MtpDatabase database) { 5961ba923ca0cb5c928a16729d0aa67b6bf4b2f027Daichi Hirono mDevice = device; 606baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono mMtpManager = mtpManager; 616baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono mResolver = resolver; 6247eb192b2704e27272ca94a95680cac40b6bff3fDaichi Hirono mDatabase = database; 636baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono } 646baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono 654e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono /** 664e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono * Queries the child documents of given parent. 674e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono * It loads the first NUM_INITIAL_ENTRIES of object info, then launches the background thread 684e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono * to load the rest. 694e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono */ 706baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono synchronized Cursor queryChildDocuments(String[] columnNames, Identifier parent) 716baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono throws IOException { 72071313e8b55e1ac3349d4f405ffff37b2ae2feb0Daichi Hirono assert parent.mDeviceId == mDevice.deviceId; 73071313e8b55e1ac3349d4f405ffff37b2ae2feb0Daichi Hirono 74d40b030ad55dfe8cb408fa35e0fb94400e2dcbb8Daichi Hirono LoaderTask task = mTaskList.findTask(parent); 756baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono if (task == null) { 7647eb192b2704e27272ca94a95680cac40b6bff3fDaichi Hirono if (parent.mDocumentId == null) { 7747eb192b2704e27272ca94a95680cac40b6bff3fDaichi Hirono throw new FileNotFoundException("Parent not found."); 7847eb192b2704e27272ca94a95680cac40b6bff3fDaichi Hirono } 7949f920fbde1dce6f789c2d0ad2df014c12e7e3acDaichi Hirono // TODO: Handle nit race around here. 8049f920fbde1dce6f789c2d0ad2df014c12e7e3acDaichi Hirono // 1. getObjectHandles. 8149f920fbde1dce6f789c2d0ad2df014c12e7e3acDaichi Hirono // 2. putNewDocument. 8249f920fbde1dce6f789c2d0ad2df014c12e7e3acDaichi Hirono // 3. startAddingChildDocuemnts. 8349f920fbde1dce6f789c2d0ad2df014c12e7e3acDaichi Hirono // 4. stopAddingChildDocuments - It removes the new document added at the step 2, 8449f920fbde1dce6f789c2d0ad2df014c12e7e3acDaichi Hirono // because it is not updated between start/stopAddingChildDocuments. 85678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono task = new LoaderTask(mMtpManager, mDatabase, mDevice.operationsSupported, parent); 86678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono task.loadObjectHandles(); 87678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono task.loadObjectInfoList(NUM_INITIAL_ENTRIES); 88d40b030ad55dfe8cb408fa35e0fb94400e2dcbb8Daichi Hirono } else { 89d40b030ad55dfe8cb408fa35e0fb94400e2dcbb8Daichi Hirono // Once remove the existing task in order to add it to the head of the list. 90d40b030ad55dfe8cb408fa35e0fb94400e2dcbb8Daichi Hirono mTaskList.remove(task); 916baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono } 926baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono 93d40b030ad55dfe8cb408fa35e0fb94400e2dcbb8Daichi Hirono mTaskList.addFirst(task); 944e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono if (task.getState() == LoaderTask.STATE_LOADING) { 954e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono resume(); 966baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono } 976baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono return task.createCursor(mResolver, columnNames); 986baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono } 996baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono 1004e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono /** 1014e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono * Resumes a background thread. 1024e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono */ 1034e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono synchronized void resume() { 1044e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono if (mBackgroundThread == null) { 1054e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono mBackgroundThread = new BackgroundLoaderThread(); 1064e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono mBackgroundThread.start(); 1074e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono } 1084e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono } 1094e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono 1104e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono /** 1114e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono * Obtains next task to be run in background thread, or release the reference to background 1124e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono * thread. 1134e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono * 1144e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono * Worker thread that receives null task needs to exit. 1154e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono */ 1164e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono @WorkerThread 1174e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono synchronized @Nullable LoaderTask getNextTaskOrReleaseBackgroundThread() { 1184e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono Preconditions.checkState(mBackgroundThread != null); 1194e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono 12076be46f4d9314fd7daca0985a0a7e02126d85975Daichi Hirono for (final LoaderTask task : mTaskList) { 12176be46f4d9314fd7daca0985a0a7e02126d85975Daichi Hirono if (task.getState() == LoaderTask.STATE_LOADING) { 12276be46f4d9314fd7daca0985a0a7e02126d85975Daichi Hirono return task; 12376be46f4d9314fd7daca0985a0a7e02126d85975Daichi Hirono } 1244e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono } 1254e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono 12661ba923ca0cb5c928a16729d0aa67b6bf4b2f027Daichi Hirono final Identifier identifier = mDatabase.getUnmappedDocumentsParent(mDevice.deviceId); 1274e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono if (identifier != null) { 1284e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono final LoaderTask existingTask = mTaskList.findTask(identifier); 1294e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono if (existingTask != null) { 1304e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono Preconditions.checkState(existingTask.getState() != LoaderTask.STATE_LOADING); 1314e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono mTaskList.remove(existingTask); 1324e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono } 133678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono final LoaderTask newTask = new LoaderTask( 134678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono mMtpManager, mDatabase, mDevice.operationsSupported, identifier); 135678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono newTask.loadObjectHandles(); 136678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono mTaskList.addFirst(newTask); 137678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono return newTask; 1384e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono } 1394e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono 1404e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono mBackgroundThread = null; 1414e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono return null; 1424e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono } 1434e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono 1444e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono /** 1454e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono * Terminates background thread. 1464e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono */ 1474e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono @Override 1484e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono public void close() throws InterruptedException { 1494e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono final Thread thread; 1504e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono synchronized (this) { 1514e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono mTaskList.clear(); 1524e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono thread = mBackgroundThread; 1534e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono } 1544e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono if (thread != null) { 1554e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono thread.interrupt(); 1564e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono thread.join(); 1574e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono } 1586baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono } 1596baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono 16081d74743107b372424fb8f7439357bdd608f8cafTomasz Mikolajewski synchronized void clearCompletedTasks() { 1614c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski mTaskList.clearCompletedTasks(); 1626baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono } 1636baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono 16476be46f4d9314fd7daca0985a0a7e02126d85975Daichi Hirono /** 16576be46f4d9314fd7daca0985a0a7e02126d85975Daichi Hirono * Cancels the task for |parentIdentifier|. 16676be46f4d9314fd7daca0985a0a7e02126d85975Daichi Hirono * 16776be46f4d9314fd7daca0985a0a7e02126d85975Daichi Hirono * Task is removed from the cached list and it will create new task when |parentIdentifier|'s 16876be46f4d9314fd7daca0985a0a7e02126d85975Daichi Hirono * children are queried next. 16976be46f4d9314fd7daca0985a0a7e02126d85975Daichi Hirono */ 17076be46f4d9314fd7daca0985a0a7e02126d85975Daichi Hirono void cancelTask(Identifier parentIdentifier) { 17176be46f4d9314fd7daca0985a0a7e02126d85975Daichi Hirono final LoaderTask task; 17276be46f4d9314fd7daca0985a0a7e02126d85975Daichi Hirono synchronized (this) { 17376be46f4d9314fd7daca0985a0a7e02126d85975Daichi Hirono task = mTaskList.findTask(parentIdentifier); 17476be46f4d9314fd7daca0985a0a7e02126d85975Daichi Hirono } 17576be46f4d9314fd7daca0985a0a7e02126d85975Daichi Hirono if (task != null) { 17676be46f4d9314fd7daca0985a0a7e02126d85975Daichi Hirono task.cancel(); 17776be46f4d9314fd7daca0985a0a7e02126d85975Daichi Hirono mTaskList.remove(task); 17876be46f4d9314fd7daca0985a0a7e02126d85975Daichi Hirono } 17981d74743107b372424fb8f7439357bdd608f8cafTomasz Mikolajewski } 18081d74743107b372424fb8f7439357bdd608f8cafTomasz Mikolajewski 1814e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono /** 1824e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono * Background thread to fetch object info. 1834e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono */ 1846baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono private class BackgroundLoaderThread extends Thread { 1854e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono /** 1864e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono * Finds task that needs to be processed, then loads NUM_LOADING_ENTRIES of object info and 1874e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono * store them to the database. If it does not find a task, exits the thread. 1884e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono */ 1896baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono @Override 1906baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono public void run() { 1916baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 1924e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono while (!Thread.interrupted()) { 1934e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono final LoaderTask task = getNextTaskOrReleaseBackgroundThread(); 1944e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono if (task == null) { 1954e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono return; 1966baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono } 197678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono task.loadObjectInfoList(NUM_LOADING_ENTRIES); 198678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono final boolean shouldNotify = 1993edcde295326db07520ed5eedde087c24c16dfd0Daichi Hirono task.getState() != LoaderTask.STATE_CANCELLED && 2003edcde295326db07520ed5eedde087c24c16dfd0Daichi Hirono (task.mLastNotified.getTime() < 2013edcde295326db07520ed5eedde087c24c16dfd0Daichi Hirono new Date().getTime() - NOTIFY_PERIOD_MS || 2023edcde295326db07520ed5eedde087c24c16dfd0Daichi Hirono task.getState() != LoaderTask.STATE_LOADING); 203678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono if (shouldNotify) { 204678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono task.notify(mResolver); 2056baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono } 2066baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono } 2076baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono } 2086baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono } 2096baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono 2104e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono /** 2114e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono * Task list that has helper methods to search/clear tasks. 2124e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono */ 213d40b030ad55dfe8cb408fa35e0fb94400e2dcbb8Daichi Hirono private static class TaskList extends LinkedList<LoaderTask> { 214d40b030ad55dfe8cb408fa35e0fb94400e2dcbb8Daichi Hirono LoaderTask findTask(Identifier parent) { 215d40b030ad55dfe8cb408fa35e0fb94400e2dcbb8Daichi Hirono for (int i = 0; i < size(); i++) { 216d40b030ad55dfe8cb408fa35e0fb94400e2dcbb8Daichi Hirono if (get(i).mIdentifier.equals(parent)) 217d40b030ad55dfe8cb408fa35e0fb94400e2dcbb8Daichi Hirono return get(i); 218d40b030ad55dfe8cb408fa35e0fb94400e2dcbb8Daichi Hirono } 219d40b030ad55dfe8cb408fa35e0fb94400e2dcbb8Daichi Hirono return null; 220d40b030ad55dfe8cb408fa35e0fb94400e2dcbb8Daichi Hirono } 221d40b030ad55dfe8cb408fa35e0fb94400e2dcbb8Daichi Hirono 2224c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski void clearCompletedTasks() { 223d40b030ad55dfe8cb408fa35e0fb94400e2dcbb8Daichi Hirono int i = 0; 224d40b030ad55dfe8cb408fa35e0fb94400e2dcbb8Daichi Hirono while (i < size()) { 22547eb192b2704e27272ca94a95680cac40b6bff3fDaichi Hirono if (get(i).getState() == LoaderTask.STATE_COMPLETED) { 226d40b030ad55dfe8cb408fa35e0fb94400e2dcbb8Daichi Hirono remove(i); 227d40b030ad55dfe8cb408fa35e0fb94400e2dcbb8Daichi Hirono } else { 228d40b030ad55dfe8cb408fa35e0fb94400e2dcbb8Daichi Hirono i++; 229d40b030ad55dfe8cb408fa35e0fb94400e2dcbb8Daichi Hirono } 230d40b030ad55dfe8cb408fa35e0fb94400e2dcbb8Daichi Hirono } 231d40b030ad55dfe8cb408fa35e0fb94400e2dcbb8Daichi Hirono } 232d40b030ad55dfe8cb408fa35e0fb94400e2dcbb8Daichi Hirono } 233d40b030ad55dfe8cb408fa35e0fb94400e2dcbb8Daichi Hirono 2344e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono /** 2354e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono * Loader task. 2364e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono * Each task is responsible for fetching child documents for the given parent document. 2374e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono */ 2386baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono private static class LoaderTask { 239678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono static final int STATE_START = 0; 240678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono static final int STATE_LOADING = 1; 241678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono static final int STATE_COMPLETED = 2; 242678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono static final int STATE_ERROR = 3; 24376be46f4d9314fd7daca0985a0a7e02126d85975Daichi Hirono static final int STATE_CANCELLED = 4; 24447eb192b2704e27272ca94a95680cac40b6bff3fDaichi Hirono 245678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono final MtpManager mManager; 24647eb192b2704e27272ca94a95680cac40b6bff3fDaichi Hirono final MtpDatabase mDatabase; 24737a655aac1d61ce2fe346531f78cbcfbf51388e9Daichi Hirono final int[] mOperationsSupported; 2486baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono final Identifier mIdentifier; 249678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono int[] mObjectHandles; 250678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono int mState; 2516baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono Date mLastNotified; 252678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono int mPosition; 253678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono IOException mError; 2546baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono 255678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono LoaderTask(MtpManager manager, MtpDatabase database, int[] operationsSupported, 256678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono Identifier identifier) { 257071313e8b55e1ac3349d4f405ffff37b2ae2feb0Daichi Hirono assert operationsSupported != null; 258071313e8b55e1ac3349d4f405ffff37b2ae2feb0Daichi Hirono assert identifier.mDocumentType != MtpDatabaseConstants.DOCUMENT_TYPE_DEVICE; 259678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono mManager = manager; 26047eb192b2704e27272ca94a95680cac40b6bff3fDaichi Hirono mDatabase = database; 26161ba923ca0cb5c928a16729d0aa67b6bf4b2f027Daichi Hirono mOperationsSupported = operationsSupported; 2626baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono mIdentifier = identifier; 263678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono mObjectHandles = null; 264678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono mState = STATE_START; 265678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono mPosition = 0; 2666baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono mLastNotified = new Date(); 2676baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono } 2686baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono 269678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono synchronized void loadObjectHandles() { 270678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono assert mState == STATE_START; 27176be46f4d9314fd7daca0985a0a7e02126d85975Daichi Hirono mPosition = 0; 272678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono int parentHandle = mIdentifier.mObjectHandle; 273678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono // Need to pass the special value MtpManager.OBJECT_HANDLE_ROOT_CHILDREN to 274678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono // getObjectHandles if we would like to obtain children under the root. 275678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono if (mIdentifier.mDocumentType == MtpDatabaseConstants.DOCUMENT_TYPE_STORAGE) { 276678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono parentHandle = MtpManager.OBJECT_HANDLE_ROOT_CHILDREN; 277678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono } 278678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono try { 279678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono mObjectHandles = mManager.getObjectHandles( 280678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono mIdentifier.mDeviceId, mIdentifier.mStorageId, parentHandle); 281678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono mState = STATE_LOADING; 282678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono } catch (IOException error) { 283678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono mError = error; 284678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono mState = STATE_ERROR; 285678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono } 286678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono } 287678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono 2884e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono /** 2894e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono * Returns a cursor that traverses the child document of the parent document handled by the 2904e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono * task. 2914e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono * The returned task may have a EXTRA_LOADING flag. 2924e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono */ 293678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono synchronized Cursor createCursor(ContentResolver resolver, String[] columnNames) 294678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono throws IOException { 2956baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono final Bundle extras = new Bundle(); 29647eb192b2704e27272ca94a95680cac40b6bff3fDaichi Hirono switch (getState()) { 29747eb192b2704e27272ca94a95680cac40b6bff3fDaichi Hirono case STATE_LOADING: 29847eb192b2704e27272ca94a95680cac40b6bff3fDaichi Hirono extras.putBoolean(DocumentsContract.EXTRA_LOADING, true); 29947eb192b2704e27272ca94a95680cac40b6bff3fDaichi Hirono break; 30047eb192b2704e27272ca94a95680cac40b6bff3fDaichi Hirono case STATE_ERROR: 301678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono throw mError; 30247eb192b2704e27272ca94a95680cac40b6bff3fDaichi Hirono } 3039e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono final Cursor cursor = 3049e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono mDatabase.queryChildDocuments(columnNames, mIdentifier.mDocumentId); 30576be46f4d9314fd7daca0985a0a7e02126d85975Daichi Hirono cursor.setExtras(extras); 3066baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono cursor.setNotificationUri(resolver, createUri()); 3076baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono return cursor; 3086baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono } 3096baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono 3104e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono /** 311678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono * Stores object information into database. 3124e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono */ 313678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono void loadObjectInfoList(int count) { 314678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono synchronized (this) { 315678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono if (mState != STATE_LOADING) { 316678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono return; 317678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono } 318678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono if (mPosition == 0) { 319678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono try{ 320678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono mDatabase.getMapper().startAddingDocuments(mIdentifier.mDocumentId); 321678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono } catch (FileNotFoundException error) { 322678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono mError = error; 323678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono mState = STATE_ERROR; 324678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono return; 325678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono } 326678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono } 327678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono } 328678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono final ArrayList<MtpObjectInfo> infoList = new ArrayList<>(); 329678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono for (int chunkEnd = mPosition + count; 330678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono mPosition < mObjectHandles.length && mPosition < chunkEnd; 331678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono mPosition++) { 332678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono try { 333678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono infoList.add(mManager.getObjectInfo( 334678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono mIdentifier.mDeviceId, mObjectHandles[mPosition])); 335678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono } catch (IOException error) { 336678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono Log.e(MtpDocumentsProvider.TAG, "Failed to load object info", error); 337678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono } 338678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono } 33964111e08d905525c7f4fe27e69953eb71bd62511Daichi Hirono final long[] objectSizeList = new long[infoList.size()]; 34064111e08d905525c7f4fe27e69953eb71bd62511Daichi Hirono for (int i = 0; i < infoList.size(); i++) { 34164111e08d905525c7f4fe27e69953eb71bd62511Daichi Hirono final MtpObjectInfo info = infoList.get(i); 34264111e08d905525c7f4fe27e69953eb71bd62511Daichi Hirono // Compressed size is 32-bit unsigned integer but getCompressedSize returns the 34364111e08d905525c7f4fe27e69953eb71bd62511Daichi Hirono // value in Java int (signed 32-bit integer). Use getCompressedSizeLong instead 34464111e08d905525c7f4fe27e69953eb71bd62511Daichi Hirono // to get the value in Java long. 34564111e08d905525c7f4fe27e69953eb71bd62511Daichi Hirono if (info.getCompressedSizeLong() != 0xffffffffl) { 34664111e08d905525c7f4fe27e69953eb71bd62511Daichi Hirono objectSizeList[i] = info.getCompressedSizeLong(); 34764111e08d905525c7f4fe27e69953eb71bd62511Daichi Hirono continue; 34864111e08d905525c7f4fe27e69953eb71bd62511Daichi Hirono } 34964111e08d905525c7f4fe27e69953eb71bd62511Daichi Hirono 35064111e08d905525c7f4fe27e69953eb71bd62511Daichi Hirono if (!MtpDeviceRecord.isSupported( 35164111e08d905525c7f4fe27e69953eb71bd62511Daichi Hirono mOperationsSupported, 35264111e08d905525c7f4fe27e69953eb71bd62511Daichi Hirono MtpConstants.OPERATION_GET_OBJECT_PROP_DESC) || 35364111e08d905525c7f4fe27e69953eb71bd62511Daichi Hirono !MtpDeviceRecord.isSupported( 35464111e08d905525c7f4fe27e69953eb71bd62511Daichi Hirono mOperationsSupported, 35564111e08d905525c7f4fe27e69953eb71bd62511Daichi Hirono MtpConstants.OPERATION_GET_OBJECT_PROP_VALUE)) { 35664111e08d905525c7f4fe27e69953eb71bd62511Daichi Hirono objectSizeList[i] = -1; 35764111e08d905525c7f4fe27e69953eb71bd62511Daichi Hirono continue; 35864111e08d905525c7f4fe27e69953eb71bd62511Daichi Hirono } 35964111e08d905525c7f4fe27e69953eb71bd62511Daichi Hirono 36064111e08d905525c7f4fe27e69953eb71bd62511Daichi Hirono // Object size is more than 4GB. 36164111e08d905525c7f4fe27e69953eb71bd62511Daichi Hirono try { 36264111e08d905525c7f4fe27e69953eb71bd62511Daichi Hirono objectSizeList[i] = mManager.getObjectSizeLong( 36364111e08d905525c7f4fe27e69953eb71bd62511Daichi Hirono mIdentifier.mDeviceId, 36464111e08d905525c7f4fe27e69953eb71bd62511Daichi Hirono info.getObjectHandle(), 36564111e08d905525c7f4fe27e69953eb71bd62511Daichi Hirono info.getFormat()); 36664111e08d905525c7f4fe27e69953eb71bd62511Daichi Hirono } catch (IOException error) { 36764111e08d905525c7f4fe27e69953eb71bd62511Daichi Hirono Log.e(MtpDocumentsProvider.TAG, "Failed to get object size property.", error); 36864111e08d905525c7f4fe27e69953eb71bd62511Daichi Hirono objectSizeList[i] = -1; 36964111e08d905525c7f4fe27e69953eb71bd62511Daichi Hirono } 37064111e08d905525c7f4fe27e69953eb71bd62511Daichi Hirono } 371678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono synchronized (this) { 37276be46f4d9314fd7daca0985a0a7e02126d85975Daichi Hirono // Check if the task is cancelled or not. 37376be46f4d9314fd7daca0985a0a7e02126d85975Daichi Hirono if (mState != STATE_LOADING) { 37476be46f4d9314fd7daca0985a0a7e02126d85975Daichi Hirono return; 37576be46f4d9314fd7daca0985a0a7e02126d85975Daichi Hirono } 376678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono try { 377678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono mDatabase.getMapper().putChildDocuments( 378678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono mIdentifier.mDeviceId, 379678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono mIdentifier.mDocumentId, 380678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono mOperationsSupported, 38164111e08d905525c7f4fe27e69953eb71bd62511Daichi Hirono infoList.toArray(new MtpObjectInfo[infoList.size()]), 38264111e08d905525c7f4fe27e69953eb71bd62511Daichi Hirono objectSizeList); 383678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono } catch (FileNotFoundException error) { 384678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono // Looks like the parent document information is removed. 385678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono // Adding documents has already cancelled in Mapper so we don't need to invoke 386678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono // stopAddingDocuments. 387678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono mError = error; 388678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono mState = STATE_ERROR; 389678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono return; 390678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono } 391678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono if (mPosition >= mObjectHandles.length) { 392678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono try{ 393678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono mDatabase.getMapper().stopAddingDocuments(mIdentifier.mDocumentId); 394678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono mState = STATE_COMPLETED; 395678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono } catch (FileNotFoundException error) { 396678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono mError = error; 397678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono mState = STATE_ERROR; 398678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono return; 399678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono } 400678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono } 40147eb192b2704e27272ca94a95680cac40b6bff3fDaichi Hirono } 4026baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono } 4036baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono 4044e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono /** 40576be46f4d9314fd7daca0985a0a7e02126d85975Daichi Hirono * Cancels the task. 40676be46f4d9314fd7daca0985a0a7e02126d85975Daichi Hirono */ 40776be46f4d9314fd7daca0985a0a7e02126d85975Daichi Hirono synchronized void cancel() { 40876be46f4d9314fd7daca0985a0a7e02126d85975Daichi Hirono mDatabase.getMapper().cancelAddingDocuments(mIdentifier.mDocumentId); 40976be46f4d9314fd7daca0985a0a7e02126d85975Daichi Hirono mState = STATE_CANCELLED; 41076be46f4d9314fd7daca0985a0a7e02126d85975Daichi Hirono } 41176be46f4d9314fd7daca0985a0a7e02126d85975Daichi Hirono 41276be46f4d9314fd7daca0985a0a7e02126d85975Daichi Hirono /** 413678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono * Returns a state of the task. 4144e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono */ 415678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono int getState() { 416678ed36bebb7b0f5ff342e9da30d693bffb8aeb2Daichi Hirono return mState; 4176baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono } 4186baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono 4194e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono /** 4204e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono * Notifies a change of child list of the document. 4214e94b8deaa646f176bad9b80d5924ce64142743eDaichi Hirono */ 4226baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono void notify(ContentResolver resolver) { 4236baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono resolver.notifyChange(createUri(), null, false); 4246baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono mLastNotified = new Date(); 4256baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono } 4266baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono 4276baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono private Uri createUri() { 4286baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono return DocumentsContract.buildChildDocumentsUri( 4299e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono MtpDocumentsProvider.AUTHORITY, mIdentifier.mDocumentId); 4306baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono } 4316baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono } 4326baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono} 433