1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.mtp;
18 
19 import android.annotation.IntDef;
20 import android.database.sqlite.SQLiteQueryBuilder;
21 import android.provider.DocumentsContract.Document;
22 import android.provider.DocumentsContract.Root;
23 
24 import java.lang.annotation.Retention;
25 import java.lang.annotation.RetentionPolicy;
26 import java.util.HashMap;
27 import java.util.Map;
28 
29 /**
30  * Class containing MtpDatabase constants.
31  */
32 class MtpDatabaseConstants {
33     static final int DATABASE_VERSION = 5;
34     static final String DATABASE_NAME = "database";
35 
36     static final int FLAG_DATABASE_IN_MEMORY = 1;
37     static final int FLAG_DATABASE_IN_FILE = 0;
38 
39     /**
40      * Table representing documents including root documents.
41      */
42     static final String TABLE_DOCUMENTS = "Documents";
43 
44     /**
45      * Table containing additional information only available for root documents.
46      * The table uses same primary keys with corresponding documents.
47      */
48     static final String TABLE_ROOT_EXTRA = "RootExtra";
49 
50     /**
51      * Table containing last boot count.
52      */
53     static final String TABLE_LAST_BOOT_COUNT = "LastBootCount";
54 
55     /**
56      * 'FROM' closure of joining TABLE_DOCUMENTS and TABLE_ROOT_EXTRA.
57      */
58     static final String JOIN_ROOTS = createJoinFromClosure(
59             TABLE_DOCUMENTS,
60             TABLE_ROOT_EXTRA,
61             Document.COLUMN_DOCUMENT_ID,
62             Root.COLUMN_ROOT_ID);
63 
64     static final String COLUMN_DEVICE_ID = "device_id";
65     static final String COLUMN_STORAGE_ID = "storage_id";
66     static final String COLUMN_OBJECT_HANDLE = "object_handle";
67     static final String COLUMN_PARENT_DOCUMENT_ID = "parent_document_id";
68     static final String COLUMN_DOCUMENT_TYPE = "document_type";
69     static final String COLUMN_ROW_STATE = "row_state";
70     static final String COLUMN_MAPPING_KEY = "mapping_key";
71 
72     /**
73      * Value for TABLE_LAST_BOOT_COUNT.
74      * Type: INTEGER
75      */
76     static final String COLUMN_VALUE = "value";
77 
78     /**
79      * The state represents that the row has a valid object handle.
80      */
81     static final int ROW_STATE_VALID = 0;
82 
83     /**
84      * The state represents that the rows added at the previous cycle and need to be updated with
85      * fresh values.
86      * The row may not have valid object handle. External application can still fetch the documents.
87      * If the external application tries to fetch object handle, the provider resolves pending
88      * documents with invalidated documents ahead.
89      */
90     static final int ROW_STATE_INVALIDATED = 1;
91 
92     /**
93      * The documents are of device/storage that are disconnected now. The documents are invisible
94      * but their document ID will be reuse when the device/storage is connected again.
95      */
96     static final int ROW_STATE_DISCONNECTED = 2;
97 
98     @IntDef(value = { DOCUMENT_TYPE_DEVICE, DOCUMENT_TYPE_STORAGE, DOCUMENT_TYPE_OBJECT })
99     @Retention(RetentionPolicy.SOURCE)
100     public @interface DocumentType {}
101 
102     /**
103      * Document that represents a MTP device.
104      */
105     static final int DOCUMENT_TYPE_DEVICE = 0;
106 
107     /**
108      * Document that represents a MTP storage.
109      */
110     static final int DOCUMENT_TYPE_STORAGE = 1;
111 
112     /**
113      * Document that represents a MTP object.
114      */
115     static final int DOCUMENT_TYPE_OBJECT = 2;
116 
117     static final String SELECTION_DOCUMENT_ID = Document.COLUMN_DOCUMENT_ID + " = ?";
118     static final String SELECTION_ROOT_ID = Root.COLUMN_ROOT_ID + " = ?";
119 
120     static final String QUERY_CREATE_DOCUMENTS =
121             "CREATE TABLE " + TABLE_DOCUMENTS + " (" +
122             Document.COLUMN_DOCUMENT_ID +
123                 " INTEGER PRIMARY KEY AUTOINCREMENT," +
124             COLUMN_DEVICE_ID + " INTEGER," +
125             COLUMN_STORAGE_ID + " INTEGER," +
126             COLUMN_OBJECT_HANDLE + " INTEGER," +
127             COLUMN_PARENT_DOCUMENT_ID + " INTEGER," +
128             COLUMN_ROW_STATE + " INTEGER NOT NULL," +
129             COLUMN_DOCUMENT_TYPE + " INTEGER NOT NULL," +
130             COLUMN_MAPPING_KEY + " STRING," +
131             Document.COLUMN_MIME_TYPE + " TEXT NOT NULL," +
132             Document.COLUMN_DISPLAY_NAME + " TEXT NOT NULL," +
133             Document.COLUMN_SUMMARY + " TEXT," +
134             Document.COLUMN_LAST_MODIFIED + " INTEGER," +
135             Document.COLUMN_ICON + " INTEGER," +
136             Document.COLUMN_FLAGS + " INTEGER NOT NULL," +
137             Document.COLUMN_SIZE + " INTEGER);";
138 
139     static final String QUERY_CREATE_ROOT_EXTRA =
140             "CREATE TABLE " + TABLE_ROOT_EXTRA + " (" +
141             Root.COLUMN_ROOT_ID + " INTEGER PRIMARY KEY," +
142             Root.COLUMN_FLAGS + " INTEGER NOT NULL," +
143             Root.COLUMN_AVAILABLE_BYTES + " INTEGER," +
144             Root.COLUMN_CAPACITY_BYTES + " INTEGER," +
145             Root.COLUMN_MIME_TYPES + " TEXT NOT NULL);";
146 
147     static final String QUERY_CREATE_LAST_BOOT_COUNT =
148             "CREATE TABLE " + TABLE_LAST_BOOT_COUNT + " (value INTEGER NOT NULL);";
149 
150     /**
151      * Map for columns names to provide DocumentContract.Root compatible columns.
152      * @see SQLiteQueryBuilder#setProjectionMap(Map)
153      */
154     static final Map<String, String> COLUMN_MAP_ROOTS;
155     static {
156         COLUMN_MAP_ROOTS = new HashMap<>();
COLUMN_MAP_ROOTS.put(Root.COLUMN_ROOT_ID, TABLE_ROOT_EXTRA + "." + Root.COLUMN_ROOT_ID)157         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)158         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)159         COLUMN_MAP_ROOTS.put(
160                 Root.COLUMN_ICON,
161                 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)162         COLUMN_MAP_ROOTS.put(
163                 Root.COLUMN_TITLE,
164                 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)165         COLUMN_MAP_ROOTS.put(
166                 Root.COLUMN_SUMMARY,
167                 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)168         COLUMN_MAP_ROOTS.put(
169                 Root.COLUMN_DOCUMENT_ID,
170                 TABLE_DOCUMENTS + "." + Document.COLUMN_DOCUMENT_ID +
171                 " AS " + Root.COLUMN_DOCUMENT_ID);
COLUMN_MAP_ROOTS.put( Root.COLUMN_AVAILABLE_BYTES, TABLE_ROOT_EXTRA + "." + Root.COLUMN_AVAILABLE_BYTES)172         COLUMN_MAP_ROOTS.put(
173                 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)174         COLUMN_MAP_ROOTS.put(
175                 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)176         COLUMN_MAP_ROOTS.put(
177                 Root.COLUMN_MIME_TYPES, TABLE_ROOT_EXTRA + "." + Root.COLUMN_MIME_TYPES);
COLUMN_MAP_ROOTS.put(COLUMN_DEVICE_ID, COLUMN_DEVICE_ID)178         COLUMN_MAP_ROOTS.put(COLUMN_DEVICE_ID, COLUMN_DEVICE_ID);
179     }
180 
createJoinFromClosure( String table1, String table2, String column1, String column2)181     private static String createJoinFromClosure(
182             String table1, String table2, String column1, String column2) {
183         return table1 + " LEFT JOIN " + table2 +
184                 " ON " + table1 + "." + column1 + " = " + table2 + "." + column2;
185     }
186 }
187