1 /* 2 ** 3 ** Copyright 2012, The Android Open Source Project 4 ** 5 ** Licensed under the Apache License, Version 2.0 (the "License"); 6 ** you may not use this file except in compliance with the License. 7 ** You may obtain a copy of the License at 8 ** 9 ** http://www.apache.org/licenses/LICENSE-2.0 10 ** 11 ** Unless required by applicable law or agreed to in writing, software 12 ** distributed under the License is distributed on an "AS IS" BASIS, 13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 ** See the License for the specific language governing permissions and 15 ** limitations under the License. 16 */ 17 18 package com.android.commands.requestsync; 19 20 import android.accounts.Account; 21 import android.content.ContentResolver; 22 import android.content.SyncRequest; 23 import android.os.Bundle; 24 25 import java.net.URISyntaxException; 26 27 public class RequestSync { 28 // agr parsing fields 29 private String[] mArgs; 30 private int mNextArg; 31 private String mCurArgData; 32 33 private int mExemptionFlag = ContentResolver.SYNC_EXEMPTION_NONE; 34 35 enum Operation { 36 REQUEST_SYNC { 37 @Override invoke(RequestSync caller)38 void invoke(RequestSync caller) { 39 final int flag = caller.mExemptionFlag; 40 caller.mExtras.putInt(ContentResolver.SYNC_VIRTUAL_EXTRAS_EXEMPTION_FLAG, flag); 41 if (flag == ContentResolver.SYNC_EXEMPTION_NONE) { 42 System.out.println( 43 "Making a sync request as a background app.\n" 44 + "Note: request may be throttled by App Standby.\n" 45 + "To override this behavior and run a sync immediately," 46 + " pass a -f or -F option (use -h for help).\n"); 47 } 48 final SyncRequest request = 49 new SyncRequest.Builder() 50 .setSyncAdapter(caller.mAccount, caller.mAuthority) 51 .setExtras(caller.mExtras) 52 .syncOnce() 53 .build(); 54 ContentResolver.requestSync(request); 55 } 56 }, 57 ADD_PERIODIC_SYNC { 58 @Override invoke(RequestSync caller)59 void invoke(RequestSync caller) { 60 ContentResolver.addPeriodicSync(caller.mAccount, caller.mAuthority, caller.mExtras, 61 caller.mPeriodicIntervalSeconds); 62 } 63 }, 64 REMOVE_PERIODIC_SYNC { 65 @Override invoke(RequestSync caller)66 void invoke(RequestSync caller) { 67 ContentResolver.removePeriodicSync( 68 caller.mAccount, caller.mAuthority, caller.mExtras); 69 } 70 }; 71 invoke(RequestSync caller)72 abstract void invoke(RequestSync caller); 73 } 74 75 private Operation mOperation; 76 77 // account & authority 78 private String mAccountName; 79 private String mAccountType; 80 private String mAuthority; 81 82 private Account mAccount; 83 84 private int mPeriodicIntervalSeconds; 85 86 // extras 87 private Bundle mExtras = new Bundle(); 88 89 /** 90 * Command-line entry point. 91 * 92 * @param args The command-line arguments 93 */ main(String[] args)94 public static void main(String[] args) { 95 try { 96 (new RequestSync()).run(args); 97 } catch (IllegalArgumentException e) { 98 showUsage(); 99 System.err.println("Error: " + e); 100 e.printStackTrace(); 101 } catch (Exception e) { 102 e.printStackTrace(System.err); 103 System.exit(1); 104 } 105 } 106 run(String[] args)107 private void run(String[] args) throws Exception { 108 mArgs = args; 109 mNextArg = 0; 110 111 final boolean ok = parseArgs(); 112 if (ok) { 113 final Account account = mAccountName != null && mAccountType != null 114 ? new Account(mAccountName, mAccountType) : null; 115 116 System.out.printf("Requesting sync for: \n"); 117 if (account != null) { 118 System.out.printf(" Account: %s (%s)\n", account.name, account.type); 119 } else { 120 System.out.printf(" Account: all\n"); 121 } 122 123 System.out.printf(" Authority: %s\n", mAuthority != null ? mAuthority : "All"); 124 125 if (mExtras.size() > 0) { 126 System.out.printf(" Extras:\n"); 127 for (String key : mExtras.keySet()) { 128 System.out.printf(" %s: %s\n", key, mExtras.get(key)); 129 } 130 } 131 132 mAccount = account; 133 134 mOperation.invoke(this); 135 } 136 } 137 parseArgs()138 private boolean parseArgs() throws URISyntaxException { 139 mOperation = Operation.REQUEST_SYNC; 140 if (mArgs.length > 0) { 141 switch (mArgs[0]) { 142 case "add-periodic": 143 mNextArg++; 144 mOperation = Operation.ADD_PERIODIC_SYNC; 145 mPeriodicIntervalSeconds = Integer.parseInt(nextArgRequired()); 146 break; 147 case "remove-periodic": 148 mNextArg++; 149 mOperation = Operation.REMOVE_PERIODIC_SYNC; 150 break; 151 } 152 } 153 154 String opt; 155 while ((opt=nextOption()) != null) { 156 if (opt.equals("-h") || opt.equals("--help")) { 157 showUsage(); 158 return false; 159 } else if (opt.equals("-n") || opt.equals("--account-name")) { 160 mAccountName = nextArgRequired(); 161 } else if (opt.equals("-t") || opt.equals("--account-type")) { 162 mAccountType = nextArgRequired(); 163 } else if (opt.equals("-a") || opt.equals("--authority")) { 164 mAuthority = nextArgRequired(); 165 } else if (opt.equals("--is") || opt.equals("--ignore-settings")) { 166 mExtras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true); 167 } else if (opt.equals("--ib") || opt.equals("--ignore-backoff")) { 168 mExtras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, true); 169 } else if (opt.equals("--dd") || opt.equals("--discard-deletions")) { 170 mExtras.putBoolean(ContentResolver.SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS, true); 171 } else if (opt.equals("--nr") || opt.equals("--no-retry")) { 172 mExtras.putBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, true); 173 } else if (opt.equals("--ex") || opt.equals("--expedited")) { 174 mExtras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true); 175 } else if (opt.equals("-i") || opt.equals("--initialize")) { 176 mExtras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true); 177 } else if (opt.equals("-m") || opt.equals("--manual")) { 178 mExtras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true); 179 } else if (opt.equals("--od") || opt.equals("--override-deletions")) { 180 mExtras.putBoolean(ContentResolver.SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS, true); 181 } else if (opt.equals("-u") || opt.equals("--upload-only")) { 182 mExtras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true); 183 } else if (opt.equals("--rc") || opt.equals("--require-charging")) { 184 mExtras.putBoolean(ContentResolver.SYNC_EXTRAS_REQUIRE_CHARGING, true); 185 } else if (opt.equals("-e") || opt.equals("--es") || opt.equals("--extra-string")) { 186 final String key = nextArgRequired(); 187 final String value = nextArgRequired(); 188 mExtras.putString(key, value); 189 } else if (opt.equals("--esn") || opt.equals("--extra-string-null")) { 190 final String key = nextArgRequired(); 191 mExtras.putString(key, null); 192 } else if (opt.equals("--ei") || opt.equals("--extra-int")) { 193 final String key = nextArgRequired(); 194 final String value = nextArgRequired(); 195 mExtras.putInt(key, Integer.valueOf(value)); 196 } else if (opt.equals("--el") || opt.equals("--extra-long")) { 197 final String key = nextArgRequired(); 198 final String value = nextArgRequired(); 199 mExtras.putLong(key, Long.parseLong(value)); 200 } else if (opt.equals("--ef") || opt.equals("--extra-float")) { 201 final String key = nextArgRequired(); 202 final String value = nextArgRequired(); 203 mExtras.putFloat(key, Long.parseLong(value)); 204 } else if (opt.equals("--ed") || opt.equals("--extra-double")) { 205 final String key = nextArgRequired(); 206 final String value = nextArgRequired(); 207 mExtras.putFloat(key, Long.parseLong(value)); 208 } else if (opt.equals("--ez") || opt.equals("--extra-bool")) { 209 final String key = nextArgRequired(); 210 final String value = nextArgRequired(); 211 mExtras.putBoolean(key, Boolean.valueOf(value)); 212 213 } else if (opt.equals("-f") || opt.equals("--foreground")) { 214 mExemptionFlag = ContentResolver.SYNC_EXEMPTION_PROMOTE_BUCKET; 215 216 } else if (opt.equals("-F") || opt.equals("--top")) { 217 mExemptionFlag = ContentResolver.SYNC_EXEMPTION_PROMOTE_BUCKET_WITH_TEMP; 218 219 } else { 220 System.err.println("Error: Unknown option: " + opt); 221 showUsage(); 222 return false; 223 } 224 } 225 226 if (mNextArg < mArgs.length) { 227 showUsage(); 228 return false; 229 } 230 return true; 231 } 232 nextOption()233 private String nextOption() { 234 if (mCurArgData != null) { 235 String prev = mArgs[mNextArg - 1]; 236 throw new IllegalArgumentException("No argument expected after \"" + prev + "\""); 237 } 238 if (mNextArg >= mArgs.length) { 239 return null; 240 } 241 String arg = mArgs[mNextArg]; 242 if (!arg.startsWith("-")) { 243 return null; 244 } 245 mNextArg++; 246 if (arg.equals("--")) { 247 return null; 248 } 249 if (arg.length() > 1 && arg.charAt(1) != '-') { 250 if (arg.length() > 2) { 251 mCurArgData = arg.substring(2); 252 return arg.substring(0, 2); 253 } else { 254 mCurArgData = null; 255 return arg; 256 } 257 } 258 mCurArgData = null; 259 return arg; 260 } 261 nextArg()262 private String nextArg() { 263 if (mCurArgData != null) { 264 String arg = mCurArgData; 265 mCurArgData = null; 266 return arg; 267 } else if (mNextArg < mArgs.length) { 268 return mArgs[mNextArg++]; 269 } else { 270 return null; 271 } 272 } 273 nextArgRequired()274 private String nextArgRequired() { 275 String arg = nextArg(); 276 if (arg == null) { 277 String prev = mArgs[mNextArg - 1]; 278 throw new IllegalArgumentException("Argument expected after \"" + prev + "\""); 279 } 280 return arg; 281 } 282 showUsage()283 private static void showUsage() { 284 System.err.println( 285 "Usage:\n" + 286 "\n" + 287 " requestsync [options]\n" + 288 " With no options, a sync will be requested for all account and all sync\n" + 289 " authorities with no extras.\n" + 290 " Basic options:\n" + 291 " -h|--help: Display this message\n" + 292 " -n|--account-name <ACCOUNT-NAME>\n" + 293 " -t|--account-type <ACCOUNT-TYPE>\n" + 294 " -a|--authority <AUTHORITY>\n" + 295 " App-standby related options\n" + 296 "\n" + 297 " -f|--foreground (cause WORKING_SET, FREQUENT sync adapters" + 298 " to run immediately)\n" + 299 " -F|--top (cause even RARE sync adapters to run immediately)\n" + 300 " ContentResolver extra options:\n" + 301 " --is|--ignore-settings: Add SYNC_EXTRAS_IGNORE_SETTINGS\n" + 302 " --ib|--ignore-backoff: Add SYNC_EXTRAS_IGNORE_BACKOFF\n" + 303 " --dd|--discard-deletions: Add SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS\n" + 304 " --nr|--no-retry: Add SYNC_EXTRAS_DO_NOT_RETRY\n" + 305 " --ex|--expedited: Add SYNC_EXTRAS_EXPEDITED\n" + 306 " -i|--initialize: Add SYNC_EXTRAS_INITIALIZE\n" + 307 " --m|--manual: Add SYNC_EXTRAS_MANUAL\n" + 308 " --od|--override-deletions: Add SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS\n" + 309 " -u|--upload-only: Add SYNC_EXTRAS_UPLOAD\n" + 310 " --rc|--require-charging: Add SYNC_EXTRAS_REQUIRE_CHARGING\n" + 311 " Custom extra options:\n" + 312 " -e|--es|--extra-string <KEY> <VALUE>\n" + 313 " --esn|--extra-string-null <KEY>\n" + 314 " --ei|--extra-int <KEY> <VALUE>\n" + 315 " --el|--extra-long <KEY> <VALUE>\n" + 316 " --ef|--extra-float <KEY> <VALUE>\n" + 317 " --ed|--extra-double <KEY> <VALUE>\n" + 318 " --ez|--extra-bool <KEY> <VALUE>\n" + 319 "\n" + 320 " requestsync add-periodic INTERVAL-SECOND [options]\n" + 321 " requestsync remove-periodic [options]\n" + 322 " Mandatory options:\n" + 323 " -n|--account-name <ACCOUNT-NAME>\n" + 324 " -t|--account-type <ACCOUNT-TYPE>\n" + 325 " -a|--authority <AUTHORITY>\n" + 326 " Also takes the above extra options.\n" 327 ); 328 } 329 } 330