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("--ej") || opt.equals("--schedule-as-ej")) { 186 mExtras.putBoolean(ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB, true); 187 } else if (opt.equals("-e") || opt.equals("--es") || opt.equals("--extra-string")) { 188 final String key = nextArgRequired(); 189 final String value = nextArgRequired(); 190 mExtras.putString(key, value); 191 } else if (opt.equals("--esn") || opt.equals("--extra-string-null")) { 192 final String key = nextArgRequired(); 193 mExtras.putString(key, null); 194 } else if (opt.equals("--ei") || opt.equals("--extra-int")) { 195 final String key = nextArgRequired(); 196 final String value = nextArgRequired(); 197 mExtras.putInt(key, Integer.valueOf(value)); 198 } else if (opt.equals("--el") || opt.equals("--extra-long")) { 199 final String key = nextArgRequired(); 200 final String value = nextArgRequired(); 201 mExtras.putLong(key, Long.parseLong(value)); 202 } else if (opt.equals("--ef") || opt.equals("--extra-float")) { 203 final String key = nextArgRequired(); 204 final String value = nextArgRequired(); 205 mExtras.putFloat(key, Long.parseLong(value)); 206 } else if (opt.equals("--ed") || opt.equals("--extra-double")) { 207 final String key = nextArgRequired(); 208 final String value = nextArgRequired(); 209 mExtras.putFloat(key, Long.parseLong(value)); 210 } else if (opt.equals("--ez") || opt.equals("--extra-bool")) { 211 final String key = nextArgRequired(); 212 final String value = nextArgRequired(); 213 mExtras.putBoolean(key, Boolean.valueOf(value)); 214 215 } else if (opt.equals("-f") || opt.equals("--foreground")) { 216 mExemptionFlag = ContentResolver.SYNC_EXEMPTION_PROMOTE_BUCKET; 217 218 } else if (opt.equals("-F") || opt.equals("--top")) { 219 mExemptionFlag = ContentResolver.SYNC_EXEMPTION_PROMOTE_BUCKET_WITH_TEMP; 220 221 } else { 222 System.err.println("Error: Unknown option: " + opt); 223 showUsage(); 224 return false; 225 } 226 } 227 228 if (mNextArg < mArgs.length) { 229 showUsage(); 230 return false; 231 } 232 return true; 233 } 234 nextOption()235 private String nextOption() { 236 if (mCurArgData != null) { 237 String prev = mArgs[mNextArg - 1]; 238 throw new IllegalArgumentException("No argument expected after \"" + prev + "\""); 239 } 240 if (mNextArg >= mArgs.length) { 241 return null; 242 } 243 String arg = mArgs[mNextArg]; 244 if (!arg.startsWith("-")) { 245 return null; 246 } 247 mNextArg++; 248 if (arg.equals("--")) { 249 return null; 250 } 251 if (arg.length() > 1 && arg.charAt(1) != '-') { 252 if (arg.length() > 2) { 253 mCurArgData = arg.substring(2); 254 return arg.substring(0, 2); 255 } else { 256 mCurArgData = null; 257 return arg; 258 } 259 } 260 mCurArgData = null; 261 return arg; 262 } 263 nextArg()264 private String nextArg() { 265 if (mCurArgData != null) { 266 String arg = mCurArgData; 267 mCurArgData = null; 268 return arg; 269 } else if (mNextArg < mArgs.length) { 270 return mArgs[mNextArg++]; 271 } else { 272 return null; 273 } 274 } 275 nextArgRequired()276 private String nextArgRequired() { 277 String arg = nextArg(); 278 if (arg == null) { 279 String prev = mArgs[mNextArg - 1]; 280 throw new IllegalArgumentException("Argument expected after \"" + prev + "\""); 281 } 282 return arg; 283 } 284 showUsage()285 private static void showUsage() { 286 System.err.println( 287 "Usage:\n" + 288 "\n" + 289 " requestsync [options]\n" + 290 " With no options, a sync will be requested for all account and all sync\n" + 291 " authorities with no extras.\n" + 292 " Basic options:\n" + 293 " -h|--help: Display this message\n" + 294 " -n|--account-name <ACCOUNT-NAME>\n" + 295 " -t|--account-type <ACCOUNT-TYPE>\n" + 296 " -a|--authority <AUTHORITY>\n" + 297 " App-standby related options\n" + 298 "\n" + 299 " -f|--foreground (defeat app-standby job throttling," + 300 " but not battery saver)\n" + 301 " -F|--top (defeat app-standby job throttling and battery saver)\n" + 302 " ContentResolver extra options:\n" + 303 " --is|--ignore-settings: Add SYNC_EXTRAS_IGNORE_SETTINGS\n" + 304 " --ib|--ignore-backoff: Add SYNC_EXTRAS_IGNORE_BACKOFF\n" + 305 " --dd|--discard-deletions: Add SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS\n" + 306 " --nr|--no-retry: Add SYNC_EXTRAS_DO_NOT_RETRY\n" + 307 " --ex|--expedited: Add SYNC_EXTRAS_EXPEDITED\n" + 308 " -i|--initialize: Add SYNC_EXTRAS_INITIALIZE\n" + 309 " --m|--manual: Add SYNC_EXTRAS_MANUAL\n" + 310 " --od|--override-deletions: Add SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS\n" + 311 " -u|--upload-only: Add SYNC_EXTRAS_UPLOAD\n" + 312 " --rc|--require-charging: Add SYNC_EXTRAS_REQUIRE_CHARGING\n" + 313 " Custom extra options:\n" + 314 " -e|--es|--extra-string <KEY> <VALUE>\n" + 315 " --esn|--extra-string-null <KEY>\n" + 316 " --ei|--extra-int <KEY> <VALUE>\n" + 317 " --el|--extra-long <KEY> <VALUE>\n" + 318 " --ef|--extra-float <KEY> <VALUE>\n" + 319 " --ed|--extra-double <KEY> <VALUE>\n" + 320 " --ez|--extra-bool <KEY> <VALUE>\n" + 321 "\n" + 322 " requestsync add-periodic INTERVAL-SECOND [options]\n" + 323 " requestsync remove-periodic [options]\n" + 324 " Mandatory options:\n" + 325 " -n|--account-name <ACCOUNT-NAME>\n" + 326 " -t|--account-type <ACCOUNT-TYPE>\n" + 327 " -a|--authority <AUTHORITY>\n" + 328 " Also takes the above extra options.\n" 329 ); 330 } 331 } 332