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