1 /* 2 * Copyright (C) 2008 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.settings.accounts; 18 19 import static android.app.admin.DevicePolicyResources.Strings.Settings.ACCESSIBILITY_PERSONAL_ACCOUNT_TITLE; 20 import static android.app.admin.DevicePolicyResources.Strings.Settings.ACCESSIBILITY_WORK_ACCOUNT_TITLE; 21 22 import android.accounts.Account; 23 import android.accounts.AccountManager; 24 import android.app.Activity; 25 import android.app.Dialog; 26 import android.app.admin.DevicePolicyManager; 27 import android.app.settings.SettingsEnums; 28 import android.content.ContentResolver; 29 import android.content.Context; 30 import android.content.Intent; 31 import android.content.IntentSender; 32 import android.content.SyncAdapterType; 33 import android.content.SyncInfo; 34 import android.content.SyncStatusInfo; 35 import android.content.pm.PackageManager; 36 import android.content.pm.ProviderInfo; 37 import android.content.pm.UserInfo; 38 import android.os.Binder; 39 import android.os.Bundle; 40 import android.os.UserHandle; 41 import android.os.UserManager; 42 import android.text.TextUtils; 43 import android.text.format.DateUtils; 44 import android.util.Log; 45 import android.view.Menu; 46 import android.view.MenuInflater; 47 import android.view.MenuItem; 48 49 import androidx.annotation.VisibleForTesting; 50 import androidx.appcompat.app.AlertDialog; 51 import androidx.preference.Preference; 52 53 import com.android.settings.R; 54 import com.android.settings.Utils; 55 import com.android.settings.widget.EntityHeaderController; 56 import com.android.settingslib.widget.FooterPreference; 57 58 import com.google.android.collect.Lists; 59 60 import java.util.ArrayList; 61 import java.util.Date; 62 import java.util.HashMap; 63 import java.util.List; 64 65 public class AccountSyncSettings extends AccountPreferenceBase { 66 67 public static final String ACCOUNT_KEY = "account"; 68 private static final int MENU_SYNC_NOW_ID = Menu.FIRST; 69 private static final int MENU_SYNC_CANCEL_ID = Menu.FIRST + 1; 70 private static final int CANT_DO_ONETIME_SYNC_DIALOG = 102; 71 private static final String UID_REQUEST_KEY = "uid_request_code"; 72 73 private Account mAccount; 74 private ArrayList<SyncAdapterType> mInvisibleAdapters = Lists.newArrayList(); 75 private HashMap<Integer, Integer> mUidRequestCodeMap = new HashMap<>(); 76 77 @Override onCreateDialog(final int id)78 public Dialog onCreateDialog(final int id) { 79 Dialog dialog = null; 80 if (id == CANT_DO_ONETIME_SYNC_DIALOG) { 81 dialog = new AlertDialog.Builder(getActivity()) 82 .setTitle(R.string.cant_sync_dialog_title) 83 .setMessage(R.string.cant_sync_dialog_message) 84 .setPositiveButton(android.R.string.ok, null) 85 .create(); 86 } 87 return dialog; 88 } 89 90 @Override getMetricsCategory()91 public int getMetricsCategory() { 92 return SettingsEnums.ACCOUNTS_ACCOUNT_SYNC; 93 } 94 95 @Override getDialogMetricsCategory(int dialogId)96 public int getDialogMetricsCategory(int dialogId) { 97 switch (dialogId) { 98 case CANT_DO_ONETIME_SYNC_DIALOG: 99 return SettingsEnums.DIALOG_ACCOUNT_SYNC_CANNOT_ONETIME_SYNC; 100 default: 101 return 0; 102 } 103 } 104 105 @Override onCreate(Bundle icicle)106 public void onCreate(Bundle icicle) { 107 super.onCreate(icicle); 108 addPreferencesFromResource(R.xml.account_sync_settings); 109 getPreferenceScreen().setOrderingAsAdded(false); 110 setAccessibilityTitle(); 111 } 112 113 @Override onSaveInstanceState(Bundle outState)114 public void onSaveInstanceState(Bundle outState) { 115 super.onSaveInstanceState(outState); 116 if (!mUidRequestCodeMap.isEmpty()) { 117 outState.putSerializable(UID_REQUEST_KEY, mUidRequestCodeMap); 118 } 119 } 120 121 @Override onActivityCreated(Bundle savedInstanceState)122 public void onActivityCreated(Bundle savedInstanceState) { 123 super.onActivityCreated(savedInstanceState); 124 125 Bundle arguments = getArguments(); 126 if (arguments == null) { 127 Log.e(TAG, "No arguments provided when starting intent. ACCOUNT_KEY needed."); 128 finish(); 129 return; 130 } 131 mAccount = arguments.getParcelable(ACCOUNT_KEY); 132 if (!accountExists(mAccount)) { 133 Log.e(TAG, "Account provided does not exist: " + mAccount); 134 finish(); 135 return; 136 } 137 if (Log.isLoggable(TAG, Log.VERBOSE)) { 138 Log.v(TAG, "Got account: " + mAccount); 139 } 140 final Activity activity = getActivity(); 141 final Preference pref = EntityHeaderController 142 .newInstance(activity, this, null /* header */) 143 .setRecyclerView(getListView(), getSettingsLifecycle()) 144 .setIcon(getDrawableForType(mAccount.type)) 145 .setLabel(mAccount.name) 146 .setSummary(getLabelForType(mAccount.type)) 147 .done(activity, getPrefContext()); 148 pref.setOrder(0); 149 getPreferenceScreen().addPreference(pref); 150 if (savedInstanceState != null && savedInstanceState.containsKey(UID_REQUEST_KEY)) { 151 mUidRequestCodeMap = (HashMap<Integer, Integer>) savedInstanceState.getSerializable( 152 UID_REQUEST_KEY); 153 } 154 } 155 setAccessibilityTitle()156 private void setAccessibilityTitle() { 157 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE); 158 UserInfo user = um.getUserInfo(mUserHandle.getIdentifier()); 159 boolean isWorkProfile = user != null ? user.isManagedProfile() : false; 160 CharSequence currentTitle = getActivity().getTitle(); 161 162 DevicePolicyManager devicePolicyManager = getSystemService(DevicePolicyManager.class); 163 164 String accessibilityTitle = 165 isWorkProfile 166 ? devicePolicyManager.getResources().getString( 167 ACCESSIBILITY_WORK_ACCOUNT_TITLE, 168 () -> getString(R.string.accessibility_work_account_title, 169 currentTitle), currentTitle) 170 : devicePolicyManager.getResources().getString( 171 ACCESSIBILITY_PERSONAL_ACCOUNT_TITLE, 172 () -> getString( 173 R.string.accessibility_personal_account_title, 174 currentTitle), currentTitle); 175 176 getActivity().setTitle(Utils.createAccessibleSequence(currentTitle, accessibilityTitle)); 177 } 178 179 @Override onResume()180 public void onResume() { 181 mAuthenticatorHelper.listenToAccountUpdates(); 182 updateAuthDescriptions(); 183 onAccountsUpdate(Binder.getCallingUserHandle()); 184 super.onResume(); 185 } 186 187 @Override onPause()188 public void onPause() { 189 super.onPause(); 190 mAuthenticatorHelper.stopListeningToAccountUpdates(); 191 } 192 addSyncStateSwitch(Account account, String authority, String packageName, int uid)193 private void addSyncStateSwitch(Account account, String authority, 194 String packageName, int uid) { 195 SyncStateSwitchPreference item = (SyncStateSwitchPreference) getCachedPreference(authority); 196 if (item == null) { 197 item = new SyncStateSwitchPreference(getPrefContext(), account, authority, 198 packageName, uid); 199 getPreferenceScreen().addPreference(item); 200 } else { 201 item.setup(account, authority, packageName, uid); 202 } 203 final PackageManager packageManager = getPackageManager(); 204 item.setPersistent(false); 205 final ProviderInfo providerInfo = packageManager.resolveContentProviderAsUser( 206 authority, 0, mUserHandle.getIdentifier()); 207 if (providerInfo == null) { 208 return; 209 } 210 final CharSequence providerLabel = providerInfo.loadLabel(packageManager); 211 if (TextUtils.isEmpty(providerLabel)) { 212 Log.e(TAG, "Provider needs a label for authority '" + authority + "'"); 213 return; 214 } 215 item.setTitle(providerLabel); 216 item.setKey(authority); 217 } 218 219 @Override onCreateOptionsMenu(Menu menu, MenuInflater inflater)220 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 221 MenuItem syncNow = menu.add(0, MENU_SYNC_NOW_ID, 0, 222 getString(R.string.sync_menu_sync_now)); 223 MenuItem syncCancel = menu.add(0, MENU_SYNC_CANCEL_ID, 0, 224 getString(R.string.sync_menu_sync_cancel)); 225 226 syncNow.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER | 227 MenuItem.SHOW_AS_ACTION_WITH_TEXT); 228 syncCancel.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER | 229 MenuItem.SHOW_AS_ACTION_WITH_TEXT); 230 231 super.onCreateOptionsMenu(menu, inflater); 232 } 233 234 @Override onPrepareOptionsMenu(Menu menu)235 public void onPrepareOptionsMenu(Menu menu) { 236 super.onPrepareOptionsMenu(menu); 237 // Note that this also counts accounts that are not currently displayed 238 boolean syncActive = !ContentResolver.getCurrentSyncsAsUser( 239 mUserHandle.getIdentifier()).isEmpty(); 240 menu.findItem(MENU_SYNC_NOW_ID).setVisible(!syncActive).setEnabled(enabledSyncNowMenu()); 241 menu.findItem(MENU_SYNC_CANCEL_ID).setVisible(syncActive); 242 } 243 244 @Override onOptionsItemSelected(MenuItem item)245 public boolean onOptionsItemSelected(MenuItem item) { 246 switch (item.getItemId()) { 247 case MENU_SYNC_NOW_ID: 248 startSyncForEnabledProviders(); 249 return true; 250 case MENU_SYNC_CANCEL_ID: 251 cancelSyncForEnabledProviders(); 252 return true; 253 } 254 return super.onOptionsItemSelected(item); 255 } 256 257 @Override onActivityResult(int requestCode, int resultCode, Intent data)258 public void onActivityResult(int requestCode, int resultCode, Intent data) { 259 if (resultCode == Activity.RESULT_OK) { 260 final int count = getPreferenceScreen().getPreferenceCount(); 261 for (int i = 0; i < count; i++) { 262 Preference preference = getPreferenceScreen().getPreference(i); 263 if (preference instanceof SyncStateSwitchPreference) { 264 SyncStateSwitchPreference syncPref = (SyncStateSwitchPreference) preference; 265 if (getRequestCodeByUid(syncPref.getUid()) == requestCode) { 266 onPreferenceTreeClick(syncPref); 267 return; 268 } 269 } 270 } 271 } 272 } 273 274 @Override onPreferenceTreeClick(Preference preference)275 public boolean onPreferenceTreeClick(Preference preference) { 276 if (getActivity() == null) { 277 return false; 278 } 279 if (preference instanceof SyncStateSwitchPreference) { 280 SyncStateSwitchPreference syncPref = (SyncStateSwitchPreference) preference; 281 final String authority = syncPref.getAuthority(); 282 if (TextUtils.isEmpty(authority)) { 283 return false; 284 } 285 final Account account = syncPref.getAccount(); 286 final int userId = mUserHandle.getIdentifier(); 287 final String packageName = syncPref.getPackageName(); 288 289 boolean syncAutomatically = ContentResolver.getSyncAutomaticallyAsUser(account, 290 authority, userId); 291 if (syncPref.isOneTimeSyncMode()) { 292 // If the sync adapter doesn't have access to the account we either 293 // request access by starting an activity if possible or kick off the 294 // sync which will end up posting an access request notification. 295 if (requestAccountAccessIfNeeded(packageName)) { 296 return true; 297 } 298 requestOrCancelSync(account, authority, true); 299 } else { 300 boolean syncOn = syncPref.isChecked(); 301 boolean oldSyncState = syncAutomatically; 302 if (syncOn != oldSyncState) { 303 // Toggling this switch triggers sync but we may need a user approval. 304 // If the sync adapter doesn't have access to the account we either 305 // request access by starting an activity if possible or kick off the 306 // sync which will end up posting an access request notification. 307 if (syncOn && requestAccountAccessIfNeeded(packageName)) { 308 return true; 309 } 310 // if we're enabling sync, this will request a sync as well 311 ContentResolver.setSyncAutomaticallyAsUser(account, authority, syncOn, userId); 312 // if the primary sync switch is off, the request above will 313 // get dropped. when the user clicks on this toggle, 314 // we want to force the sync, however. 315 if (!ContentResolver.getMasterSyncAutomaticallyAsUser(userId) || !syncOn) { 316 requestOrCancelSync(account, authority, syncOn); 317 } 318 } 319 } 320 return true; 321 } else { 322 return super.onPreferenceTreeClick(preference); 323 } 324 } 325 requestAccountAccessIfNeeded(String packageName)326 private boolean requestAccountAccessIfNeeded(String packageName) { 327 if (packageName == null) { 328 return false; 329 } 330 331 final int uid; 332 try { 333 uid = getContext().getPackageManager().getPackageUidAsUser( 334 packageName, mUserHandle.getIdentifier()); 335 } catch (PackageManager.NameNotFoundException e) { 336 Log.e(TAG, "Invalid sync ", e); 337 return false; 338 } 339 340 AccountManager accountManager = getContext().getSystemService(AccountManager.class); 341 if (!accountManager.hasAccountAccess(mAccount, packageName, mUserHandle)) { 342 IntentSender intent = accountManager.createRequestAccountAccessIntentSenderAsUser( 343 mAccount, packageName, mUserHandle); 344 if (intent != null) { 345 try { 346 final int requestCode = addUidAndGenerateRequestCode(uid); 347 startIntentSenderForResult(intent, requestCode, null /* fillInIntent */, 0, 0, 348 0, null /* options */); 349 return true; 350 } catch (IntentSender.SendIntentException e) { 351 Log.e(TAG, "Error requesting account access", e); 352 } 353 } 354 } 355 return false; 356 } 357 startSyncForEnabledProviders()358 private void startSyncForEnabledProviders() { 359 requestOrCancelSyncForEnabledProviders(true /* start them */); 360 final Activity activity = getActivity(); 361 if (activity != null) { 362 activity.invalidateOptionsMenu(); 363 } 364 } 365 cancelSyncForEnabledProviders()366 private void cancelSyncForEnabledProviders() { 367 requestOrCancelSyncForEnabledProviders(false /* cancel them */); 368 final Activity activity = getActivity(); 369 if (activity != null) { 370 activity.invalidateOptionsMenu(); 371 } 372 } 373 requestOrCancelSyncForEnabledProviders(boolean startSync)374 private void requestOrCancelSyncForEnabledProviders(boolean startSync) { 375 // sync everything that the user has enabled 376 int count = getPreferenceScreen().getPreferenceCount(); 377 for (int i = 0; i < count; i++) { 378 Preference pref = getPreferenceScreen().getPreference(i); 379 if (!(pref instanceof SyncStateSwitchPreference)) { 380 continue; 381 } 382 SyncStateSwitchPreference syncPref = (SyncStateSwitchPreference) pref; 383 if (!syncPref.isChecked()) { 384 continue; 385 } 386 requestOrCancelSync(syncPref.getAccount(), syncPref.getAuthority(), startSync); 387 } 388 // plus whatever the system needs to sync, e.g., invisible sync adapters 389 if (mAccount != null) { 390 for (SyncAdapterType syncAdapter : mInvisibleAdapters) { 391 requestOrCancelSync(mAccount, syncAdapter.authority, startSync); 392 } 393 } 394 } 395 requestOrCancelSync(Account account, String authority, boolean flag)396 private void requestOrCancelSync(Account account, String authority, boolean flag) { 397 if (flag) { 398 Bundle extras = new Bundle(); 399 extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true); 400 ContentResolver.requestSyncAsUser(account, authority, mUserHandle.getIdentifier(), 401 extras); 402 } else { 403 ContentResolver.cancelSyncAsUser(account, authority, mUserHandle.getIdentifier()); 404 } 405 } 406 isSyncing(List<SyncInfo> currentSyncs, Account account, String authority)407 private boolean isSyncing(List<SyncInfo> currentSyncs, Account account, String authority) { 408 for (SyncInfo syncInfo : currentSyncs) { 409 if (syncInfo.account.equals(account) && syncInfo.authority.equals(authority)) { 410 return true; 411 } 412 } 413 return false; 414 } 415 416 @Override onSyncStateUpdated()417 protected void onSyncStateUpdated() { 418 if (!isResumed()) return; 419 setFeedsState(); 420 final Activity activity = getActivity(); 421 if (activity != null) { 422 activity.invalidateOptionsMenu(); 423 } 424 } 425 setFeedsState()426 private void setFeedsState() { 427 // iterate over all the preferences, setting the state properly for each 428 Date date = new Date(); 429 final int userId = mUserHandle.getIdentifier(); 430 List<SyncInfo> currentSyncs = ContentResolver.getCurrentSyncsAsUser(userId); 431 boolean syncIsFailing = false; 432 433 // Refresh the sync status switches - some syncs may have become active. 434 updateAccountSwitches(); 435 436 for (int i = 0, count = getPreferenceScreen().getPreferenceCount(); i < count; i++) { 437 Preference pref = getPreferenceScreen().getPreference(i); 438 if (!(pref instanceof SyncStateSwitchPreference)) { 439 continue; 440 } 441 SyncStateSwitchPreference syncPref = (SyncStateSwitchPreference) pref; 442 443 String authority = syncPref.getAuthority(); 444 Account account = syncPref.getAccount(); 445 446 SyncStatusInfo status = ContentResolver.getSyncStatusAsUser(account, authority, userId); 447 boolean syncEnabled = ContentResolver.getSyncAutomaticallyAsUser(account, authority, 448 userId); 449 boolean authorityIsPending = status == null ? false : status.pending; 450 boolean initialSync = status == null ? false : status.initialize; 451 452 boolean activelySyncing = isSyncing(currentSyncs, account, authority); 453 boolean lastSyncFailed = status != null 454 && status.lastFailureTime != 0 455 && status.getLastFailureMesgAsInt(0) 456 != ContentResolver.SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS; 457 if (!syncEnabled) lastSyncFailed = false; 458 if (lastSyncFailed && !activelySyncing && !authorityIsPending) { 459 syncIsFailing = true; 460 } 461 if (Log.isLoggable(TAG, Log.DEBUG)) { 462 Log.d(TAG, "Update sync status: " + account + " " + authority + 463 " active = " + activelySyncing + " pend =" + authorityIsPending); 464 } 465 466 final long successEndTime = (status == null) ? 0 : status.lastSuccessTime; 467 if (!syncEnabled) { 468 syncPref.setSummary(R.string.sync_disabled); 469 } else if (activelySyncing) { 470 syncPref.setSummary(R.string.sync_in_progress); 471 } else if (successEndTime != 0) { 472 date.setTime(successEndTime); 473 final String timeString = formatSyncDate(getContext(), date); 474 syncPref.setSummary(getResources().getString(R.string.last_synced, timeString)); 475 } else { 476 syncPref.setSummary(""); 477 } 478 int syncState = ContentResolver.getIsSyncableAsUser(account, authority, userId); 479 480 syncPref.setActive(activelySyncing && (syncState >= 0) && 481 !initialSync); 482 syncPref.setPending(authorityIsPending && (syncState >= 0) && 483 !initialSync); 484 485 syncPref.setFailed(lastSyncFailed); 486 final boolean oneTimeSyncMode = !ContentResolver.getMasterSyncAutomaticallyAsUser( 487 userId); 488 syncPref.setOneTimeSyncMode(oneTimeSyncMode); 489 syncPref.setChecked(oneTimeSyncMode || syncEnabled); 490 } 491 if (syncIsFailing) { 492 getPreferenceScreen().addPreference(new FooterPreference.Builder( 493 getActivity()).setTitle(R.string.sync_is_failing).build()); 494 } 495 } 496 497 @Override onAccountsUpdate(final UserHandle userHandle)498 public void onAccountsUpdate(final UserHandle userHandle) { 499 super.onAccountsUpdate(userHandle); 500 if (!accountExists(mAccount)) { 501 // The account was deleted 502 finish(); 503 return; 504 } 505 updateAccountSwitches(); 506 onSyncStateUpdated(); 507 } 508 accountExists(Account account)509 private boolean accountExists(Account account) { 510 if (account == null) return false; 511 512 Account[] accounts = AccountManager.get(getActivity()).getAccountsByTypeAsUser( 513 account.type, mUserHandle); 514 final int count = accounts.length; 515 for (int i = 0; i < count; i++) { 516 if (accounts[i].equals(account)) { 517 return true; 518 } 519 } 520 return false; 521 } 522 updateAccountSwitches()523 private void updateAccountSwitches() { 524 mInvisibleAdapters.clear(); 525 526 SyncAdapterType[] syncAdapters = ContentResolver.getSyncAdapterTypesAsUser( 527 mUserHandle.getIdentifier()); 528 ArrayList<SyncAdapterType> authorities = new ArrayList<>(); 529 for (int i = 0, n = syncAdapters.length; i < n; i++) { 530 final SyncAdapterType sa = syncAdapters[i]; 531 // Only keep track of sync adapters for this account 532 if (!sa.accountType.equals(mAccount.type)) continue; 533 if (sa.isUserVisible()) { 534 if (Log.isLoggable(TAG, Log.DEBUG)) { 535 Log.d(TAG, "updateAccountSwitches: added authority " + sa.authority 536 + " to accountType " + sa.accountType); 537 } 538 authorities.add(sa); 539 } else { 540 // keep track of invisible sync adapters, so sync now forces 541 // them to sync as well. 542 mInvisibleAdapters.add(sa); 543 } 544 } 545 546 if (Log.isLoggable(TAG, Log.DEBUG)) { 547 Log.d(TAG, "looking for sync adapters that match account " + mAccount); 548 } 549 550 cacheRemoveAllPrefs(getPreferenceScreen()); 551 getCachedPreference(EntityHeaderController.PREF_KEY_APP_HEADER); 552 for (int j = 0, m = authorities.size(); j < m; j++) { 553 final SyncAdapterType syncAdapter = authorities.get(j); 554 // We could check services here.... 555 int syncState = ContentResolver.getIsSyncableAsUser(mAccount, syncAdapter.authority, 556 mUserHandle.getIdentifier()); 557 if (Log.isLoggable(TAG, Log.DEBUG)) { 558 Log.d(TAG, " found authority " + syncAdapter.authority + " " + syncState); 559 } 560 if (syncState > 0) { 561 final int uid; 562 try { 563 uid = getContext().getPackageManager().getPackageUidAsUser( 564 syncAdapter.getPackageName(), mUserHandle.getIdentifier()); 565 addSyncStateSwitch(mAccount, syncAdapter.authority, 566 syncAdapter.getPackageName(), uid); 567 } catch (PackageManager.NameNotFoundException e) { 568 Log.e(TAG, "No uid for package" + syncAdapter.getPackageName(), e); 569 } 570 } 571 } 572 removeCachedPrefs(getPreferenceScreen()); 573 } 574 575 @Override getHelpResource()576 public int getHelpResource() { 577 return R.string.help_url_accounts; 578 } 579 580 @VisibleForTesting enabledSyncNowMenu()581 boolean enabledSyncNowMenu() { 582 boolean enabled = false; 583 for (int i = 0, count = getPreferenceScreen().getPreferenceCount(); i < count; i++) { 584 final Preference pref = getPreferenceScreen().getPreference(i); 585 if (!(pref instanceof SyncStateSwitchPreference)) { 586 continue; 587 } 588 final SyncStateSwitchPreference syncPref = (SyncStateSwitchPreference) pref; 589 if (syncPref.isChecked()) { 590 enabled = true; 591 break; 592 } 593 } 594 return enabled; 595 } 596 formatSyncDate(Context context, Date date)597 private static String formatSyncDate(Context context, Date date) { 598 return DateUtils.formatDateTime(context, date.getTime(), 599 DateUtils.FORMAT_SHOW_DATE 600 | DateUtils.FORMAT_SHOW_YEAR 601 | DateUtils.FORMAT_SHOW_TIME); 602 } 603 addUidAndGenerateRequestCode(int uid)604 private int addUidAndGenerateRequestCode(int uid) { 605 if (mUidRequestCodeMap.containsKey(uid)) { 606 return mUidRequestCodeMap.get(uid); 607 } 608 final int requestCode = mUidRequestCodeMap.size() + 1; 609 mUidRequestCodeMap.put(uid, requestCode); 610 return requestCode; 611 } 612 getRequestCodeByUid(int uid)613 private int getRequestCodeByUid(int uid) { 614 if (!mUidRequestCodeMap.containsKey(uid)) { 615 return -1; 616 } 617 return mUidRequestCodeMap.get(uid); 618 } 619 } 620