• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 ** Copyright 2012, 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.commands.content;
18 
19 import android.app.ActivityManager;
20 import android.app.ContentProviderHolder;
21 import android.app.IActivityManager;
22 import android.content.ContentResolver;
23 import android.content.ContentValues;
24 import android.content.IContentProvider;
25 import android.database.Cursor;
26 import android.net.Uri;
27 import android.os.Binder;
28 import android.os.Bundle;
29 import android.os.FileUtils;
30 import android.os.IBinder;
31 import android.os.ParcelFileDescriptor;
32 import android.os.Process;
33 import android.os.UserHandle;
34 import android.text.TextUtils;
35 
36 import java.io.FileDescriptor;
37 
38 /**
39  * This class is a command line utility for manipulating content. A client
40  * can insert, update, and remove records in a content provider. For example,
41  * some settings may be configured before running the CTS tests, etc.
42  * <p>
43  * Examples:
44  * <ul>
45  * <li>
46  * # Add "new_setting" secure setting with value "new_value".</br>
47  * adb shell content insert --uri content://settings/secure --bind name:s:new_setting
48  *  --bind value:s:new_value
49  * </li>
50  * <li>
51  * # Change "new_setting" secure setting to "newer_value" (You have to escape single quotes in
52  * the where clause).</br>
53  * adb shell content update --uri content://settings/secure --bind value:s:newer_value
54  *  --where "name=\'new_setting\'"
55  * </li>
56  * <li>
57  * # Remove "new_setting" secure setting.</br>
58  * adb shell content delete --uri content://settings/secure --where "name=\'new_setting\'"
59  * </li>
60  * <li>
61  * # Query \"name\" and \"value\" columns from secure settings where \"name\" is equal to"
62  *    \"new_setting\" and sort the result by name in ascending order.\n"
63  * adb shell content query --uri content://settings/secure --projection name:value
64  *  --where "name=\'new_setting\'" --sort \"name ASC\"
65  * </li>
66  * </ul>
67  * </p>
68  */
69 public class Content {
70 
71     private static final String USAGE =
72             "usage: adb shell content [subcommand] [options]\n"
73                     + "\n"
74                     + "usage: adb shell content insert --uri <URI> [--user <USER_ID>]"
75                     + " --bind <BINDING> [--bind <BINDING>...]\n"
76                     + "  <URI> a content provider URI.\n"
77                     + "  <BINDING> binds a typed value to a column and is formatted:\n"
78                     + "  <COLUMN_NAME>:<TYPE>:<COLUMN_VALUE> where:\n"
79                     + "  <TYPE> specifies data type such as:\n"
80                     + "  b - boolean, s - string, i - integer, l - long, f - float, d - double, n - null\n"
81                     + "  Note: Omit the value for passing an empty string, e.g column:s:\n"
82                     + "  Example:\n"
83                     + "  # Add \"new_setting\" secure setting with value \"new_value\".\n"
84                     + "  adb shell content insert --uri content://settings/secure --bind name:s:new_setting"
85                     + " --bind value:s:new_value\n"
86                     + "\n"
87                     + "usage: adb shell content update --uri <URI> [--user <USER_ID>] [--where <WHERE>]\n"
88                     + "  <WHERE> is a SQL style where clause in quotes (You have to escape single quotes"
89                     + " - see example below).\n"
90                     + "  Example:\n"
91                     + "  # Change \"new_setting\" secure setting to \"newer_value\".\n"
92                     + "  adb shell content update --uri content://settings/secure --bind"
93                     + " value:s:newer_value --where \"name=\'new_setting\'\"\n"
94                     + "\n"
95                     + "usage: adb shell content delete --uri <URI> [--user <USER_ID>] --bind <BINDING>"
96                     + " [--bind <BINDING>...] [--where <WHERE>]\n"
97                     + "  Example:\n"
98                     + "  # Remove \"new_setting\" secure setting.\n"
99                     + "  adb shell content delete --uri content://settings/secure "
100                     + "--where \"name=\'new_setting\'\"\n"
101                     + "\n"
102                     + "usage: adb shell content query --uri <URI> [--user <USER_ID>]"
103                     + " [--projection <PROJECTION>] [--where <WHERE>] [--sort <SORT_ORDER>]\n"
104                     + "  <PROJECTION> is a list of colon separated column names and is formatted:\n"
105                     + "  <COLUMN_NAME>[:<COLUMN_NAME>...]\n"
106                     + "  <SORT_ORDER> is the order in which rows in the result should be sorted.\n"
107                     + "  Example:\n"
108                     + "  # Select \"name\" and \"value\" columns from secure settings where \"name\" is "
109                     + "equal to \"new_setting\" and sort the result by name in ascending order.\n"
110                     + "  adb shell content query --uri content://settings/secure --projection name:value"
111                     + " --where \"name=\'new_setting\'\" --sort \"name ASC\"\n"
112                     + "\n"
113                     + "usage: adb shell content call --uri <URI> --method <METHOD> [--arg <ARG>]\n"
114                     + "       [--extra <BINDING> ...]\n"
115                     + "  <METHOD> is the name of a provider-defined method\n"
116                     + "  <ARG> is an optional string argument\n"
117                     + "  <BINDING> is like --bind above, typed data of the form <KEY>:{b,s,i,l,f,d}:<VAL>\n"
118                     + "\n"
119                     + "usage: adb shell content read --uri <URI> [--user <USER_ID>]\n"
120                     + "  Example:\n"
121                     + "  adb shell 'content read --uri content://settings/system/ringtone_cache' > host.ogg\n"
122                     + "\n"
123                     + "usage: adb shell content write --uri <URI> [--user <USER_ID>]\n"
124                     + "  Example:\n"
125                     + "  adb shell 'content write --uri content://settings/system/ringtone_cache' < host.ogg\n"
126                     + "\n"
127                     + "usage: adb shell content gettype --uri <URI> [--user <USER_ID>]\n"
128                     + "  Example:\n"
129                     + "  adb shell content gettype --uri content://media/internal/audio/media/\n"
130                     + "\n";
131 
132     private static class Parser {
133         private static final String ARGUMENT_INSERT = "insert";
134         private static final String ARGUMENT_DELETE = "delete";
135         private static final String ARGUMENT_UPDATE = "update";
136         private static final String ARGUMENT_QUERY = "query";
137         private static final String ARGUMENT_CALL = "call";
138         private static final String ARGUMENT_READ = "read";
139         private static final String ARGUMENT_WRITE = "write";
140         private static final String ARGUMENT_GET_TYPE = "gettype";
141         private static final String ARGUMENT_WHERE = "--where";
142         private static final String ARGUMENT_BIND = "--bind";
143         private static final String ARGUMENT_URI = "--uri";
144         private static final String ARGUMENT_USER = "--user";
145         private static final String ARGUMENT_PROJECTION = "--projection";
146         private static final String ARGUMENT_SORT = "--sort";
147         private static final String ARGUMENT_METHOD = "--method";
148         private static final String ARGUMENT_ARG = "--arg";
149         private static final String ARGUMENT_EXTRA = "--extra";
150         private static final String TYPE_BOOLEAN = "b";
151         private static final String TYPE_STRING = "s";
152         private static final String TYPE_INTEGER = "i";
153         private static final String TYPE_LONG = "l";
154         private static final String TYPE_FLOAT = "f";
155         private static final String TYPE_DOUBLE = "d";
156         private static final String TYPE_NULL = "n";
157         private static final String COLON = ":";
158         private static final String ARGUMENT_PREFIX = "--";
159 
160         private final Tokenizer mTokenizer;
161 
Parser(String[] args)162         public Parser(String[] args) {
163             mTokenizer = new Tokenizer(args);
164         }
165 
parseCommand()166         public Command parseCommand() {
167             try {
168                 String operation = mTokenizer.nextArg();
169                 if (ARGUMENT_INSERT.equals(operation)) {
170                     return parseInsertCommand();
171                 } else if (ARGUMENT_DELETE.equals(operation)) {
172                     return parseDeleteCommand();
173                 } else if (ARGUMENT_UPDATE.equals(operation)) {
174                     return parseUpdateCommand();
175                 } else if (ARGUMENT_QUERY.equals(operation)) {
176                     return parseQueryCommand();
177                 } else if (ARGUMENT_CALL.equals(operation)) {
178                     return parseCallCommand();
179                 } else if (ARGUMENT_READ.equals(operation)) {
180                     return parseReadCommand();
181                 } else if (ARGUMENT_WRITE.equals(operation)) {
182                     return parseWriteCommand();
183                 } else if (ARGUMENT_GET_TYPE.equals(operation)) {
184                     return parseGetTypeCommand();
185                 } else {
186                     throw new IllegalArgumentException("Unsupported operation: " + operation);
187                 }
188             } catch (IllegalArgumentException iae) {
189                 System.out.println(USAGE);
190                 System.out.println("[ERROR] " + iae.getMessage());
191                 return null;
192             }
193         }
194 
parseInsertCommand()195         private InsertCommand parseInsertCommand() {
196             Uri uri = null;
197             int userId = UserHandle.USER_SYSTEM;
198             ContentValues values = new ContentValues();
199             for (String argument; (argument = mTokenizer.nextArg()) != null;) {
200                 if (ARGUMENT_URI.equals(argument)) {
201                     uri = Uri.parse(argumentValueRequired(argument));
202                 } else if (ARGUMENT_USER.equals(argument)) {
203                     userId = Integer.parseInt(argumentValueRequired(argument));
204                 } else if (ARGUMENT_BIND.equals(argument)) {
205                     parseBindValue(values);
206                 } else {
207                     throw new IllegalArgumentException("Unsupported argument: " + argument);
208                 }
209             }
210             if (uri == null) {
211                 throw new IllegalArgumentException("Content provider URI not specified."
212                         + " Did you specify --uri argument?");
213             }
214             if (values.size() == 0) {
215                 throw new IllegalArgumentException("Bindings not specified."
216                         + " Did you specify --bind argument(s)?");
217             }
218             return new InsertCommand(uri, userId, values);
219         }
220 
parseDeleteCommand()221         private DeleteCommand parseDeleteCommand() {
222             Uri uri = null;
223             int userId = UserHandle.USER_SYSTEM;
224             String where = null;
225             for (String argument; (argument = mTokenizer.nextArg())!= null;) {
226                 if (ARGUMENT_URI.equals(argument)) {
227                     uri = Uri.parse(argumentValueRequired(argument));
228                 } else if (ARGUMENT_USER.equals(argument)) {
229                     userId = Integer.parseInt(argumentValueRequired(argument));
230                 } else if (ARGUMENT_WHERE.equals(argument)) {
231                     where = argumentValueRequired(argument);
232                 } else {
233                     throw new IllegalArgumentException("Unsupported argument: " + argument);
234                 }
235             }
236             if (uri == null) {
237                 throw new IllegalArgumentException("Content provider URI not specified."
238                         + " Did you specify --uri argument?");
239             }
240             return new DeleteCommand(uri, userId, where);
241         }
242 
parseUpdateCommand()243         private UpdateCommand parseUpdateCommand() {
244             Uri uri = null;
245             int userId = UserHandle.USER_SYSTEM;
246             String where = null;
247             ContentValues values = new ContentValues();
248             for (String argument; (argument = mTokenizer.nextArg())!= null;) {
249                 if (ARGUMENT_URI.equals(argument)) {
250                     uri = Uri.parse(argumentValueRequired(argument));
251                 } else if (ARGUMENT_USER.equals(argument)) {
252                     userId = Integer.parseInt(argumentValueRequired(argument));
253                 } else if (ARGUMENT_WHERE.equals(argument)) {
254                     where = argumentValueRequired(argument);
255                 } else if (ARGUMENT_BIND.equals(argument)) {
256                     parseBindValue(values);
257                 } else {
258                     throw new IllegalArgumentException("Unsupported argument: " + argument);
259                 }
260             }
261             if (uri == null) {
262                 throw new IllegalArgumentException("Content provider URI not specified."
263                         + " Did you specify --uri argument?");
264             }
265             if (values.size() == 0) {
266                 throw new IllegalArgumentException("Bindings not specified."
267                         + " Did you specify --bind argument(s)?");
268             }
269             return new UpdateCommand(uri, userId, values, where);
270         }
271 
parseCallCommand()272         public CallCommand parseCallCommand() {
273             String method = null;
274             int userId = UserHandle.USER_SYSTEM;
275             String arg = null;
276             Uri uri = null;
277             ContentValues values = new ContentValues();
278             for (String argument; (argument = mTokenizer.nextArg())!= null;) {
279                 if (ARGUMENT_URI.equals(argument)) {
280                     uri = Uri.parse(argumentValueRequired(argument));
281                 } else if (ARGUMENT_USER.equals(argument)) {
282                     userId = Integer.parseInt(argumentValueRequired(argument));
283                 } else if (ARGUMENT_METHOD.equals(argument)) {
284                     method = argumentValueRequired(argument);
285                 } else if (ARGUMENT_ARG.equals(argument)) {
286                     arg = argumentValueRequired(argument);
287                 } else if (ARGUMENT_EXTRA.equals(argument)) {
288                     parseBindValue(values);
289                 } else {
290                     throw new IllegalArgumentException("Unsupported argument: " + argument);
291                 }
292 
293             }
294             if (uri == null) {
295                 throw new IllegalArgumentException("Content provider URI not specified."
296                         + " Did you specify --uri argument?");
297             }
298             if (method == null) {
299                 throw new IllegalArgumentException("Content provider method not specified.");
300             }
301             return new CallCommand(uri, userId, method, arg, values);
302         }
303 
parseGetTypeCommand()304         private GetTypeCommand parseGetTypeCommand() {
305             Uri uri = null;
306             int userId = UserHandle.USER_SYSTEM;
307 
308             for (String argument; (argument = mTokenizer.nextArg()) != null;) {
309                 if (ARGUMENT_URI.equals(argument)) {
310                     uri = Uri.parse(argumentValueRequired(argument));
311                 } else if (ARGUMENT_USER.equals(argument)) {
312                     userId = Integer.parseInt(argumentValueRequired(argument));
313                 } else {
314                     throw new IllegalArgumentException("Unsupported argument: " + argument);
315                 }
316             }
317             if (uri == null) {
318                 throw new IllegalArgumentException("Content provider URI not specified."
319                         + " Did you specify --uri argument?");
320             }
321             return new GetTypeCommand(uri, userId);
322         }
323 
parseReadCommand()324         private ReadCommand parseReadCommand() {
325             Uri uri = null;
326             int userId = UserHandle.USER_SYSTEM;
327             for (String argument; (argument = mTokenizer.nextArg())!= null;) {
328                 if (ARGUMENT_URI.equals(argument)) {
329                     uri = Uri.parse(argumentValueRequired(argument));
330                 } else if (ARGUMENT_USER.equals(argument)) {
331                     userId = Integer.parseInt(argumentValueRequired(argument));
332                 } else {
333                     throw new IllegalArgumentException("Unsupported argument: " + argument);
334                 }
335             }
336             if (uri == null) {
337                 throw new IllegalArgumentException("Content provider URI not specified."
338                         + " Did you specify --uri argument?");
339             }
340             return new ReadCommand(uri, userId);
341         }
342 
parseWriteCommand()343         private WriteCommand parseWriteCommand() {
344             Uri uri = null;
345             int userId = UserHandle.USER_SYSTEM;
346             for (String argument; (argument = mTokenizer.nextArg())!= null;) {
347                 if (ARGUMENT_URI.equals(argument)) {
348                     uri = Uri.parse(argumentValueRequired(argument));
349                 } else if (ARGUMENT_USER.equals(argument)) {
350                     userId = Integer.parseInt(argumentValueRequired(argument));
351                 } else {
352                     throw new IllegalArgumentException("Unsupported argument: " + argument);
353                 }
354             }
355             if (uri == null) {
356                 throw new IllegalArgumentException("Content provider URI not specified."
357                         + " Did you specify --uri argument?");
358             }
359             return new WriteCommand(uri, userId);
360         }
361 
parseQueryCommand()362         public QueryCommand parseQueryCommand() {
363             Uri uri = null;
364             int userId = UserHandle.USER_SYSTEM;
365             String[] projection = null;
366             String sort = null;
367             String where = null;
368             for (String argument; (argument = mTokenizer.nextArg())!= null;) {
369                 if (ARGUMENT_URI.equals(argument)) {
370                     uri = Uri.parse(argumentValueRequired(argument));
371                 } else if (ARGUMENT_USER.equals(argument)) {
372                     userId = Integer.parseInt(argumentValueRequired(argument));
373                 } else if (ARGUMENT_WHERE.equals(argument)) {
374                     where = argumentValueRequired(argument);
375                 } else if (ARGUMENT_SORT.equals(argument)) {
376                     sort = argumentValueRequired(argument);
377                 } else if (ARGUMENT_PROJECTION.equals(argument)) {
378                     projection = argumentValueRequired(argument).split("[\\s]*:[\\s]*");
379                 } else {
380                     throw new IllegalArgumentException("Unsupported argument: " + argument);
381                 }
382             }
383             if (uri == null) {
384                 throw new IllegalArgumentException("Content provider URI not specified."
385                         + " Did you specify --uri argument?");
386             }
387             return new QueryCommand(uri, userId, projection, where, sort);
388         }
389 
parseBindValue(ContentValues values)390         private void parseBindValue(ContentValues values) {
391             String argument = mTokenizer.nextArg();
392             if (TextUtils.isEmpty(argument)) {
393                 throw new IllegalArgumentException("Binding not well formed: " + argument);
394             }
395             final int firstColonIndex = argument.indexOf(COLON);
396             if (firstColonIndex < 0) {
397                 throw new IllegalArgumentException("Binding not well formed: " + argument);
398             }
399             final int secondColonIndex = argument.indexOf(COLON, firstColonIndex + 1);
400             if (secondColonIndex < 0) {
401                 throw new IllegalArgumentException("Binding not well formed: " + argument);
402             }
403             String column = argument.substring(0, firstColonIndex);
404             String type = argument.substring(firstColonIndex + 1, secondColonIndex);
405             String value = argument.substring(secondColonIndex + 1);
406             if (TYPE_STRING.equals(type)) {
407                 values.put(column, value);
408             } else if (TYPE_BOOLEAN.equalsIgnoreCase(type)) {
409                 values.put(column, Boolean.parseBoolean(value));
410             } else if (TYPE_INTEGER.equalsIgnoreCase(type) || TYPE_LONG.equalsIgnoreCase(type)) {
411                 values.put(column, Long.parseLong(value));
412             } else if (TYPE_FLOAT.equalsIgnoreCase(type) || TYPE_DOUBLE.equalsIgnoreCase(type)) {
413                 values.put(column, Double.parseDouble(value));
414             } else if (TYPE_NULL.equalsIgnoreCase(type)) {
415                 values.putNull(column);
416             } else {
417                 throw new IllegalArgumentException("Unsupported type: " + type);
418             }
419         }
420 
argumentValueRequired(String argument)421         private String argumentValueRequired(String argument) {
422             String value = mTokenizer.nextArg();
423             if (TextUtils.isEmpty(value) || value.startsWith(ARGUMENT_PREFIX)) {
424                 throw new IllegalArgumentException("No value for argument: " + argument);
425             }
426             return value;
427         }
428     }
429 
430     private static class Tokenizer {
431         private final String[] mArgs;
432         private int mNextArg;
433 
Tokenizer(String[] args)434         public Tokenizer(String[] args) {
435             mArgs = args;
436         }
437 
nextArg()438         private String nextArg() {
439             if (mNextArg < mArgs.length) {
440                 return mArgs[mNextArg++];
441             } else {
442                 return null;
443             }
444         }
445     }
446 
447     private static abstract class Command {
448         final Uri mUri;
449         final int mUserId;
450 
Command(Uri uri, int userId)451         public Command(Uri uri, int userId) {
452             mUri = uri;
453             mUserId = userId;
454         }
455 
execute()456         public final void execute() {
457             String providerName = mUri.getAuthority();
458             try {
459                 IActivityManager activityManager = ActivityManager.getService();
460                 IContentProvider provider = null;
461                 IBinder token = new Binder();
462                 try {
463                     ContentProviderHolder holder = activityManager.getContentProviderExternal(
464                             providerName, mUserId, token, "*cmd*");
465                     if (holder == null) {
466                         throw new IllegalStateException("Could not find provider: " + providerName);
467                     }
468                     provider = holder.provider;
469                     onExecute(provider);
470                 } finally {
471                     if (provider != null) {
472                         activityManager.removeContentProviderExternalAsUser(
473                                 providerName, token, mUserId);
474                     }
475                 }
476             } catch (Exception e) {
477                 System.err.println("Error while accessing provider:" + providerName);
478                 e.printStackTrace();
479             }
480         }
481 
resolveCallingPackage()482         public static String resolveCallingPackage() {
483             switch (Process.myUid()) {
484                 case Process.ROOT_UID: {
485                     return "root";
486                 }
487 
488                 case Process.SHELL_UID: {
489                     return "com.android.shell";
490                 }
491 
492                 default: {
493                     return null;
494                 }
495             }
496         }
497 
onExecute(IContentProvider provider)498         protected abstract void onExecute(IContentProvider provider) throws Exception;
499     }
500 
501     private static class InsertCommand extends Command {
502         final ContentValues mContentValues;
503 
InsertCommand(Uri uri, int userId, ContentValues contentValues)504         public InsertCommand(Uri uri, int userId, ContentValues contentValues) {
505             super(uri, userId);
506             mContentValues = contentValues;
507         }
508 
509         @Override
onExecute(IContentProvider provider)510         public void onExecute(IContentProvider provider) throws Exception {
511             provider.insert(resolveCallingPackage(), mUri, mContentValues);
512         }
513     }
514 
515     private static class DeleteCommand extends Command {
516         final String mWhere;
517 
DeleteCommand(Uri uri, int userId, String where)518         public DeleteCommand(Uri uri, int userId, String where) {
519             super(uri, userId);
520             mWhere = where;
521         }
522 
523         @Override
onExecute(IContentProvider provider)524         public void onExecute(IContentProvider provider) throws Exception {
525             provider.delete(resolveCallingPackage(), mUri, mWhere, null);
526         }
527     }
528 
529     private static class CallCommand extends Command {
530         final String mMethod, mArg;
531         Bundle mExtras = null;
532 
CallCommand(Uri uri, int userId, String method, String arg, ContentValues values)533         public CallCommand(Uri uri, int userId, String method, String arg, ContentValues values) {
534             super(uri, userId);
535             mMethod = method;
536             mArg = arg;
537             if (values != null) {
538                 mExtras = new Bundle();
539                 for (String key : values.keySet()) {
540                     final Object val = values.get(key);
541                     if (val instanceof String) {
542                         mExtras.putString(key, (String) val);
543                     } else if (val instanceof Float) {
544                         mExtras.putFloat(key, (Float) val);
545                     } else if (val instanceof Double) {
546                         mExtras.putDouble(key, (Double) val);
547                     } else if (val instanceof Boolean) {
548                         mExtras.putBoolean(key, (Boolean) val);
549                     } else if (val instanceof Integer) {
550                         mExtras.putInt(key, (Integer) val);
551                     } else if (val instanceof Long) {
552                         mExtras.putLong(key, (Long) val);
553                     }
554                 }
555             }
556         }
557 
558         @Override
onExecute(IContentProvider provider)559         public void onExecute(IContentProvider provider) throws Exception {
560             Bundle result = provider.call(null, mUri.getAuthority(), mMethod, mArg, mExtras);
561             if (result != null) {
562                 result.size(); // unpack
563             }
564             System.out.println("Result: " + result);
565         }
566     }
567 
568     private static class GetTypeCommand extends Command {
GetTypeCommand(Uri uri, int userId)569         public GetTypeCommand(Uri uri, int userId) {
570             super(uri, userId);
571         }
572 
573         @Override
onExecute(IContentProvider provider)574         public void onExecute(IContentProvider provider) throws Exception {
575             String type = provider.getType(mUri);
576             System.out.println("Result: " + type);
577         }
578     }
579 
580     private static class ReadCommand extends Command {
ReadCommand(Uri uri, int userId)581         public ReadCommand(Uri uri, int userId) {
582             super(uri, userId);
583         }
584 
585         @Override
onExecute(IContentProvider provider)586         public void onExecute(IContentProvider provider) throws Exception {
587             try (ParcelFileDescriptor fd = provider.openFile(null, mUri, "r", null, null)) {
588                 FileUtils.copy(fd.getFileDescriptor(), FileDescriptor.out);
589             }
590         }
591     }
592 
593     private static class WriteCommand extends Command {
WriteCommand(Uri uri, int userId)594         public WriteCommand(Uri uri, int userId) {
595             super(uri, userId);
596         }
597 
598         @Override
onExecute(IContentProvider provider)599         public void onExecute(IContentProvider provider) throws Exception {
600             try (ParcelFileDescriptor fd = provider.openFile(null, mUri, "w", null, null)) {
601                 FileUtils.copy(FileDescriptor.in, fd.getFileDescriptor());
602             }
603         }
604     }
605 
606     private static class QueryCommand extends DeleteCommand {
607         final String[] mProjection;
608         final String mSortOrder;
609 
QueryCommand( Uri uri, int userId, String[] projection, String where, String sortOrder)610         public QueryCommand(
611                 Uri uri, int userId, String[] projection, String where, String sortOrder) {
612             super(uri, userId, where);
613             mProjection = projection;
614             mSortOrder = sortOrder;
615         }
616 
617         @Override
onExecute(IContentProvider provider)618         public void onExecute(IContentProvider provider) throws Exception {
619             Cursor cursor = provider.query(resolveCallingPackage(), mUri, mProjection,
620                     ContentResolver.createSqlQueryBundle(mWhere, null, mSortOrder), null);
621             if (cursor == null) {
622                 System.out.println("No result found.");
623                 return;
624             }
625             try {
626                 if (cursor.moveToFirst()) {
627                     int rowIndex = 0;
628                     StringBuilder builder = new StringBuilder();
629                     do {
630                         builder.setLength(0);
631                         builder.append("Row: ").append(rowIndex).append(" ");
632                         rowIndex++;
633                         final int columnCount = cursor.getColumnCount();
634                         for (int i = 0; i < columnCount; i++) {
635                             if (i > 0) {
636                                 builder.append(", ");
637                             }
638                             String columnName = cursor.getColumnName(i);
639                             String columnValue = null;
640                             final int columnIndex = cursor.getColumnIndex(columnName);
641                             final int type = cursor.getType(columnIndex);
642                             switch (type) {
643                                 case Cursor.FIELD_TYPE_FLOAT:
644                                     columnValue = String.valueOf(cursor.getFloat(columnIndex));
645                                     break;
646                                 case Cursor.FIELD_TYPE_INTEGER:
647                                     columnValue = String.valueOf(cursor.getLong(columnIndex));
648                                     break;
649                                 case Cursor.FIELD_TYPE_STRING:
650                                     columnValue = cursor.getString(columnIndex);
651                                     break;
652                                 case Cursor.FIELD_TYPE_BLOB:
653                                     columnValue = "BLOB";
654                                     break;
655                                 case Cursor.FIELD_TYPE_NULL:
656                                     columnValue = "NULL";
657                                     break;
658                             }
659                             builder.append(columnName).append("=").append(columnValue);
660                         }
661                         System.out.println(builder);
662                     } while (cursor.moveToNext());
663                 } else {
664                     System.out.println("No result found.");
665                 }
666             } finally {
667                 cursor.close();
668             }
669         }
670     }
671 
672     private static class UpdateCommand extends InsertCommand {
673         final String mWhere;
674 
UpdateCommand(Uri uri, int userId, ContentValues contentValues, String where)675         public UpdateCommand(Uri uri, int userId, ContentValues contentValues, String where) {
676             super(uri, userId, contentValues);
677             mWhere = where;
678         }
679 
680         @Override
onExecute(IContentProvider provider)681         public void onExecute(IContentProvider provider) throws Exception {
682             provider.update(resolveCallingPackage(), mUri, mContentValues, mWhere, null);
683         }
684     }
685 
main(String[] args)686     public static void main(String[] args) {
687         Parser parser = new Parser(args);
688         Command command = parser.parseCommand();
689         if (command != null) {
690             command.execute();
691         }
692     }
693 }
694