/** * doDbUpgrade is triggered by someone calling the static method "processPackageReplacedBroadcast" * * @param cps - a String array containing all the ContentProvider authorities to upgrade * @return */ protected boolean doDbUpgrade(final Intent intent) { boolean success = false; try { // First set the global lock on the Base ContentProvider LogUtils.i(LogUtils.TAG, "Package has been replaced, perform database upgrades..."); // PIMContentProviderBase.setMaintenanceLock(cpLock, true); // Get the list of content authorities from the Intent extras Bundle extras = intent.getExtras(); String[] cps = extras.getStringArray(CP_LIST); if (cps == null) { // Nothing to upgrade? return success; } // Write out our current task information to a preference file writeActionState(DB_UPGRADE_ACTION, cps); // Lock all providers, then upgrade, then unlock lockProviders(cps); success = upgradeProviders(cps); unlockProviders(cps); } catch (Exception e) { LogUtils.e(LogUtils.TAG, "Database upgrade exception: %s", e.getMessage()); } finally { // Unlock the Base Content Provider // PIMContentProviderBase.setMaintenanceLock(cpLock, false); } LogUtils.i(LogUtils.TAG, "Package has been replaced, perform database upgrades...done"); return success; }
/** * onWakefulHandleIntent called by the WakefullIntent service any time service is called * * @param intext - the intent we need to handle * @return boolean */ @Override protected boolean onWakefulHandleIntent(Intent intent) { LogUtils.i( LogUtils.TAG, "CPMaintenanceService - onWakefulHandleIntent for %s", intent.getAction()); boolean success = false; int action = getAction(intent.getAction()); switch (action) { case DB_MAINT_ACTION: break; case DB_UPGRADE_ACTION: success = doDbUpgrade(intent); break; case DB_LOCALE_ACTION: break; case START_ACTION: success = doStartupAction(readActionState()); break; } if (success) { // Overwrite the preference object to indicate we are not in the middle of a task writeActionState(UNKNOWN_ACTION, null); } LogUtils.i(LogUtils.TAG, "Current Maintenance state value is %d", readActionState()); return success; }
@Override public void onPageFinished(WebView view, String url) { // Ignore unsafe calls made after a fragment is detached from an activity. // This method needs to, for example, get at the loader manager, which needs // the fragment to be added. if (!isAdded()) { LogUtils.d( LOG_TAG, "ignoring SCVF.onPageFinished, url=%s fragment=%s", url, SecureConversationViewFragment.this); return; } if (isUserVisible()) { onConversationSeen(); } mViewController.dismissLoadingStatus(); final Set<String> emailAddresses = Sets.newHashSet(); final List<Address> cacheCopy; synchronized (mAddressCache) { cacheCopy = ImmutableList.copyOf(mAddressCache.values()); } for (Address addr : cacheCopy) { emailAddresses.add(addr.getAddress()); } final ContactLoaderCallbacks callbacks = getContactInfoSource(); callbacks.setSenders(emailAddresses); getLoaderManager().restartLoader(CONTACT_LOADER, Bundle.EMPTY, callbacks); }
private ConversationMessage getMessageFromCursor(MessageCursor cursor) { // ignore cursors that are still loading results if (cursor == null || !cursor.isLoaded()) { LogUtils.i(LOG_TAG, "CONV RENDER: existing cursor is null, rendering from scratch"); return null; } if (mActivity == null || mActivity.isFinishing()) { // Activity is finishing, just bail. return null; } if (!cursor.moveToFirst()) { LogUtils.e(LOG_TAG, "unable to open message cursor"); return null; } return cursor.getMessage(); }
/** * doStartupAction - during startup, check if a previous task was not finished * * @param action - last action performed * @return always returns true */ protected boolean doStartupAction(int lastAction) { LogUtils.i(LogUtils.TAG, "Last Maintenance state value is %d", lastAction); if (lastAction != UNKNOWN_ACTION) { queueUnfinishedTask(lastAction); } return true; }
/** * processPackageReplacedBroadcast should be called when the MY_PACKAGE_REPLACED broadcast is * received by this application * * @param context * @param broadcastIntext - the raw broadcast * @param cps - a list of ContentProvider Authorities to upgrade * @return */ public static void processPackageReplacedBroadcast( Context context, Intent broadcastIntent, String[] cps) { LogUtils.i(LogUtils.TAG, "CPMaintenanceService - package replaced"); Intent i = new Intent(context, CPMaintenanceService.class); i.setAction(ACTION_PACKAGE_REPLACED_BROADCAST); i.putExtra(Intent.EXTRA_INTENT, broadcastIntent); i.putExtra(CP_LIST, cps); context.startService(i); }
/** * queueUnfinishedTask - If the last action did not complete the queue up again * * @param lastAction - the last action saved to the prefs file * @return N/A */ protected void queueUnfinishedTask(int lastAction) { switch (lastAction) { case DB_UPGRADE_ACTION: // Read in the list of authorities String[] cps = getCPListFromSharedPrefs(); // Schedule the upgrade task if (cps != null) { LogUtils.w(LogUtils.TAG, "Previous Database upgrade task failed:"); for (String cp : cps) { LogUtils.i(LogUtils.TAG, " upgrading CP: %s", cp); } processPackageReplacedBroadcast(getApplicationContext(), null, cps); } else { LogUtils.i(LogUtils.TAG, "Last task was a db upgrade but could not find CP list"); } break; } }
/** * readActionState - read in state from preference file * * @return current action from the preference file */ protected int readActionState() { int currentAction = UNKNOWN_ACTION; try { SharedPreferences prefs = getApplicationContext().getSharedPreferences(CPMAINT_STATE_PREFERENCE, MODE_PRIVATE); if (prefs != null) { currentAction = prefs.getInt(CURRENT_TASK, UNKNOWN_ACTION); } } catch (Exception e) { LogUtils.e(LogUtils.TAG, "Unable to get CP Maint action: %s", e.getMessage()); } return currentAction; }
/** * writeActionState - write out some state to a preference file * * @param action - integer * @return N/A */ protected void writeActionState(int action, String[] cps) { try { SharedPreferences prefs = getApplicationContext().getSharedPreferences(CPMAINT_STATE_PREFERENCE, MODE_PRIVATE); SharedPreferences.Editor editPrefs = prefs.edit(); editPrefs.putInt(CURRENT_TASK, action); if (cps != null) { // Save the list of ContentProvider authority strings to the shared pref Set<String> mySet = new HashSet<String>(Arrays.asList(cps)); editPrefs.putStringSet(CP_LIST, mySet); } editPrefs.commit(); } catch (Exception e) { LogUtils.e(LogUtils.TAG, "Unable to save CP Maint action: %s", e.getMessage()); } }
/** * getActionState - write out some state to a preference file * * @return current action from the preference file */ protected String[] getCPListFromSharedPrefs() { try { SharedPreferences prefs = getApplicationContext().getSharedPreferences(CPMAINT_STATE_PREFERENCE, MODE_PRIVATE); if (prefs != null) { Set<String> cps = prefs.getStringSet(CP_LIST, null); if (cps != null) { // Convert the set to an array of String objects return cps.toArray(new String[cps.size()]); } } } catch (Exception e) { LogUtils.e( LogUtils.TAG, "Cannot get ContentProvider list from preferences: %s", e.getMessage()); } return null; }
/** * Get the final moves that we want to upsync to the server, setting the status in the DB for all * rows to {@link #STATUS_PROCESSING} that are being updated and to {@link #STATUS_FAILED} for any * old updates. Messages whose sequence of pending moves results in a no-op (i.e. the message has * been moved back to its original folder) have their moves cleared from the DB without any * upsync. * * @param context A {@link Context}. * @param accountId The account we want to update. * @return The final moves to send to the server, or null if there are none. */ public static List<MessageMove> getMoves(final Context context, final long accountId) { final ContentResolver cr = context.getContentResolver(); final Cursor c = getCursor(cr, CONTENT_URI, ProjectionMoveQuery.PROJECTION, accountId); if (c == null) { return null; } // Collapse any rows in the cursor that are acting on the same message. We know the cursor // returned by getRowsToProcess is ordered from oldest to newest, and we use this fact to // get the original and final folder for the message. LongSparseArray<MessageMove> movesMap = new LongSparseArray(); try { while (c.moveToNext()) { final long id = c.getLong(ProjectionMoveQuery.COLUMN_ID); final long messageKey = c.getLong(ProjectionMoveQuery.COLUMN_MESSAGE_KEY); final String serverId = c.getString(ProjectionMoveQuery.COLUMN_SERVER_ID); final long srcFolderKey = c.getLong(ProjectionMoveQuery.COLUMN_SRC_FOLDER_KEY); final long dstFolderKey = c.getLong(ProjectionMoveQuery.COLUMN_DST_FOLDER_KEY); final String srcFolderServerId = c.getString(ProjectionMoveQuery.COLUMN_SRC_FOLDER_SERVER_ID); final String dstFolderServerId = c.getString(ProjectionMoveQuery.COLUMN_DST_FOLDER_SERVER_ID); final MessageMove existingMove = movesMap.get(messageKey); if (existingMove != null) { if (existingMove.mLastId >= id) { LogUtils.w(LOG_TAG, "Moves were not in ascending id order"); } if (!existingMove.mDstFolderServerId.equals(srcFolderServerId) || existingMove.mDstFolderKey != srcFolderKey) { LogUtils.w(LOG_TAG, "existing move's dst not same as this move's src"); } existingMove.mDstFolderKey = dstFolderKey; existingMove.mDstFolderServerId = dstFolderServerId; existingMove.mLastId = id; } else { movesMap.put( messageKey, new MessageMove( messageKey, serverId, id, srcFolderKey, dstFolderKey, srcFolderServerId, dstFolderServerId)); } } } finally { c.close(); } // Prune any no-op moves (i.e. messages that have been moved back to the initial folder). final int moveCount = movesMap.size(); final long[] unmovedMessages = new long[moveCount]; int unmovedMessagesCount = 0; final ArrayList<MessageMove> moves = new ArrayList(moveCount); for (int i = 0; i < movesMap.size(); ++i) { final MessageMove move = movesMap.valueAt(i); // We also treat changes without a server id as a no-op. if ((move.mServerId == null || move.mServerId.length() == 0) || move.mSrcFolderKey == move.mDstFolderKey) { unmovedMessages[unmovedMessagesCount] = move.mMessageKey; ++unmovedMessagesCount; } else { moves.add(move); } } if (unmovedMessagesCount != 0) { deleteRowsForMessages(cr, CONTENT_URI, unmovedMessages, unmovedMessagesCount); } if (moves.isEmpty()) { return null; } return moves; }
/** * processStartupTask should be called when the very first ContentProvider is created is received * by this application * * @param context * @param broadcastIntext - the raw broadcast * @param cps - a list of ContentProvider Authorities to upgrade * @return */ public static void processStartupTask(Context context) { LogUtils.i(LogUtils.TAG, "CPMaintenanceService - startup of first ContentProvider"); Intent i = new Intent(context, CPMaintenanceService.class); i.setAction(ACTION_NORMAL_START); context.startService(i); }