When rebooting a device, applicaitons lose temporary URI permissions so
we don't need to keep document ID that are not granted persistent URI
permissions.
1. Check Settings.Global.BOOT_COUNT to find out if it's first time to
launch MtpDocumentsProvider since booting.
2. If so, invokes clean up method of MtpDatabase.
BUG=26212981
Change-Id: Ic9a8ca7e7a9cac1ed91fdfb01e9dce14ce819243
187 lines
7.2 KiB
Java
187 lines
7.2 KiB
Java
/*
|
|
* Copyright (C) 2015 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.android.mtp;
|
|
|
|
import android.annotation.IntDef;
|
|
import android.database.sqlite.SQLiteQueryBuilder;
|
|
import android.provider.DocumentsContract.Document;
|
|
import android.provider.DocumentsContract.Root;
|
|
|
|
import java.lang.annotation.Retention;
|
|
import java.lang.annotation.RetentionPolicy;
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
|
|
/**
|
|
* Class containing MtpDatabase constants.
|
|
*/
|
|
class MtpDatabaseConstants {
|
|
static final int DATABASE_VERSION = 5;
|
|
static final String DATABASE_NAME = "database";
|
|
|
|
static final int FLAG_DATABASE_IN_MEMORY = 1;
|
|
static final int FLAG_DATABASE_IN_FILE = 0;
|
|
|
|
/**
|
|
* Table representing documents including root documents.
|
|
*/
|
|
static final String TABLE_DOCUMENTS = "Documents";
|
|
|
|
/**
|
|
* Table containing additional information only available for root documents.
|
|
* The table uses same primary keys with corresponding documents.
|
|
*/
|
|
static final String TABLE_ROOT_EXTRA = "RootExtra";
|
|
|
|
/**
|
|
* Table containing last boot count.
|
|
*/
|
|
static final String TABLE_LAST_BOOT_COUNT = "LastBootCount";
|
|
|
|
/**
|
|
* 'FROM' closure of joining TABLE_DOCUMENTS and TABLE_ROOT_EXTRA.
|
|
*/
|
|
static final String JOIN_ROOTS = createJoinFromClosure(
|
|
TABLE_DOCUMENTS,
|
|
TABLE_ROOT_EXTRA,
|
|
Document.COLUMN_DOCUMENT_ID,
|
|
Root.COLUMN_ROOT_ID);
|
|
|
|
static final String COLUMN_DEVICE_ID = "device_id";
|
|
static final String COLUMN_STORAGE_ID = "storage_id";
|
|
static final String COLUMN_OBJECT_HANDLE = "object_handle";
|
|
static final String COLUMN_PARENT_DOCUMENT_ID = "parent_document_id";
|
|
static final String COLUMN_DOCUMENT_TYPE = "document_type";
|
|
static final String COLUMN_ROW_STATE = "row_state";
|
|
static final String COLUMN_MAPPING_KEY = "mapping_key";
|
|
|
|
/**
|
|
* Value for TABLE_LAST_BOOT_COUNT.
|
|
* Type: INTEGER
|
|
*/
|
|
static final String COLUMN_VALUE = "value";
|
|
|
|
/**
|
|
* The state represents that the row has a valid object handle.
|
|
*/
|
|
static final int ROW_STATE_VALID = 0;
|
|
|
|
/**
|
|
* The state represents that the rows added at the previous cycle and need to be updated with
|
|
* fresh values.
|
|
* The row may not have valid object handle. External application can still fetch the documents.
|
|
* If the external application tries to fetch object handle, the provider resolves pending
|
|
* documents with invalidated documents ahead.
|
|
*/
|
|
static final int ROW_STATE_INVALIDATED = 1;
|
|
|
|
/**
|
|
* The documents are of device/storage that are disconnected now. The documents are invisible
|
|
* but their document ID will be reuse when the device/storage is connected again.
|
|
*/
|
|
static final int ROW_STATE_DISCONNECTED = 2;
|
|
|
|
@IntDef(value = { DOCUMENT_TYPE_DEVICE, DOCUMENT_TYPE_STORAGE, DOCUMENT_TYPE_OBJECT })
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
public @interface DocumentType {}
|
|
|
|
/**
|
|
* Document that represents a MTP device.
|
|
*/
|
|
static final int DOCUMENT_TYPE_DEVICE = 0;
|
|
|
|
/**
|
|
* Document that represents a MTP storage.
|
|
*/
|
|
static final int DOCUMENT_TYPE_STORAGE = 1;
|
|
|
|
/**
|
|
* Document that represents a MTP object.
|
|
*/
|
|
static final int DOCUMENT_TYPE_OBJECT = 2;
|
|
|
|
static final String SELECTION_DOCUMENT_ID = Document.COLUMN_DOCUMENT_ID + " = ?";
|
|
static final String SELECTION_ROOT_ID = Root.COLUMN_ROOT_ID + " = ?";
|
|
|
|
static final String QUERY_CREATE_DOCUMENTS =
|
|
"CREATE TABLE " + TABLE_DOCUMENTS + " (" +
|
|
Document.COLUMN_DOCUMENT_ID +
|
|
" INTEGER PRIMARY KEY AUTOINCREMENT," +
|
|
COLUMN_DEVICE_ID + " INTEGER," +
|
|
COLUMN_STORAGE_ID + " INTEGER," +
|
|
COLUMN_OBJECT_HANDLE + " INTEGER," +
|
|
COLUMN_PARENT_DOCUMENT_ID + " INTEGER," +
|
|
COLUMN_ROW_STATE + " INTEGER NOT NULL," +
|
|
COLUMN_DOCUMENT_TYPE + " INTEGER NOT NULL," +
|
|
COLUMN_MAPPING_KEY + " STRING," +
|
|
Document.COLUMN_MIME_TYPE + " TEXT NOT NULL," +
|
|
Document.COLUMN_DISPLAY_NAME + " TEXT NOT NULL," +
|
|
Document.COLUMN_SUMMARY + " TEXT," +
|
|
Document.COLUMN_LAST_MODIFIED + " INTEGER," +
|
|
Document.COLUMN_ICON + " INTEGER," +
|
|
Document.COLUMN_FLAGS + " INTEGER NOT NULL," +
|
|
Document.COLUMN_SIZE + " INTEGER);";
|
|
|
|
static final String QUERY_CREATE_ROOT_EXTRA =
|
|
"CREATE TABLE " + TABLE_ROOT_EXTRA + " (" +
|
|
Root.COLUMN_ROOT_ID + " INTEGER PRIMARY KEY," +
|
|
Root.COLUMN_FLAGS + " INTEGER NOT NULL," +
|
|
Root.COLUMN_AVAILABLE_BYTES + " INTEGER," +
|
|
Root.COLUMN_CAPACITY_BYTES + " INTEGER," +
|
|
Root.COLUMN_MIME_TYPES + " TEXT NOT NULL);";
|
|
|
|
static final String QUERY_CREATE_LAST_BOOT_COUNT =
|
|
"CREATE TABLE " + TABLE_LAST_BOOT_COUNT + " (value INTEGER NOT NULL);";
|
|
|
|
/**
|
|
* Map for columns names to provide DocumentContract.Root compatible columns.
|
|
* @see SQLiteQueryBuilder#setProjectionMap(Map)
|
|
*/
|
|
static final Map<String, String> COLUMN_MAP_ROOTS;
|
|
static {
|
|
COLUMN_MAP_ROOTS = new HashMap<>();
|
|
COLUMN_MAP_ROOTS.put(Root.COLUMN_ROOT_ID, TABLE_ROOT_EXTRA + "." + Root.COLUMN_ROOT_ID);
|
|
COLUMN_MAP_ROOTS.put(Root.COLUMN_FLAGS, TABLE_ROOT_EXTRA + "." + Root.COLUMN_FLAGS);
|
|
COLUMN_MAP_ROOTS.put(
|
|
Root.COLUMN_ICON,
|
|
TABLE_DOCUMENTS + "." + Document.COLUMN_ICON + " AS " + Root.COLUMN_ICON);
|
|
COLUMN_MAP_ROOTS.put(
|
|
Root.COLUMN_TITLE,
|
|
TABLE_DOCUMENTS + "." + Document.COLUMN_DISPLAY_NAME + " AS " + Root.COLUMN_TITLE);
|
|
COLUMN_MAP_ROOTS.put(
|
|
Root.COLUMN_SUMMARY,
|
|
TABLE_DOCUMENTS + "." + Document.COLUMN_SUMMARY + " AS " + Root.COLUMN_SUMMARY);
|
|
COLUMN_MAP_ROOTS.put(
|
|
Root.COLUMN_DOCUMENT_ID,
|
|
TABLE_DOCUMENTS + "." + Document.COLUMN_DOCUMENT_ID +
|
|
" AS " + Root.COLUMN_DOCUMENT_ID);
|
|
COLUMN_MAP_ROOTS.put(
|
|
Root.COLUMN_AVAILABLE_BYTES, TABLE_ROOT_EXTRA + "." + Root.COLUMN_AVAILABLE_BYTES);
|
|
COLUMN_MAP_ROOTS.put(
|
|
Root.COLUMN_CAPACITY_BYTES, TABLE_ROOT_EXTRA + "." + Root.COLUMN_CAPACITY_BYTES);
|
|
COLUMN_MAP_ROOTS.put(
|
|
Root.COLUMN_MIME_TYPES, TABLE_ROOT_EXTRA + "." + Root.COLUMN_MIME_TYPES);
|
|
COLUMN_MAP_ROOTS.put(COLUMN_DEVICE_ID, COLUMN_DEVICE_ID);
|
|
}
|
|
|
|
private static String createJoinFromClosure(
|
|
String table1, String table2, String column1, String column2) {
|
|
return table1 + " LEFT JOIN " + table2 +
|
|
" ON " + table1 + "." + column1 + " = " + table2 + "." + column2;
|
|
}
|
|
}
|