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