diff options
4 files changed, 124 insertions, 65 deletions
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java index d3067ec..47a6ede 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -51,11 +51,13 @@ import com.android.internal.backup.LocalTransport; import com.android.internal.backup.GoogleTransport; import com.android.internal.backup.IBackupTransport; +import java.io.EOFException; import java.io.File; import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintWriter; +import java.io.RandomAccessFile; import java.lang.String; import java.util.ArrayList; import java.util.HashMap; @@ -122,6 +124,9 @@ class BackupManagerService extends IBackupManager.Stub { private File mStateDir; private File mDataDir; + private File mJournalDir; + private File mJournal; + private RandomAccessFile mJournalStream; public BackupManagerService(Context context) { mContext = context; @@ -133,6 +138,11 @@ class BackupManagerService extends IBackupManager.Stub { mStateDir.mkdirs(); mDataDir = Environment.getDownloadCacheDirectory(); + // Set up the backup-request journaling + mJournalDir = new File(mStateDir, "pending"); + mJournalDir.mkdirs(); + makeJournalLocked(); // okay because no other threads are running yet + //!!! TODO: default to cloud transport, not local mTransportId = BackupManager.TRANSPORT_LOCAL; @@ -141,6 +151,10 @@ class BackupManagerService extends IBackupManager.Stub { addPackageParticipantsLocked(null); } + // Now that we know about valid backup participants, parse any + // leftover journal files and schedule a new backup pass + parseLeftoverJournals(); + // Register for broadcasts about package install, etc., so we can // update the provider list. IntentFilter filter = new IntentFilter(); @@ -150,6 +164,46 @@ class BackupManagerService extends IBackupManager.Stub { mContext.registerReceiver(mBroadcastReceiver, filter); } + private void makeJournalLocked() { + try { + mJournal = File.createTempFile("journal", null, mJournalDir); + mJournalStream = new RandomAccessFile(mJournal, "rwd"); + } catch (IOException e) { + Log.e(TAG, "Unable to write backup journals"); + mJournal = null; + mJournalStream = null; + } + } + + private void parseLeftoverJournals() { + if (mJournal != null) { + File[] allJournals = mJournalDir.listFiles(); + for (File f : allJournals) { + if (f.compareTo(mJournal) != 0) { + // This isn't the current journal, so it must be a leftover. Read + // out the package names mentioned there and schedule them for + // backup. + try { + Log.i(TAG, "Found stale backup journal, scheduling:"); + RandomAccessFile in = new RandomAccessFile(f, "r"); + while (true) { + String packageName = in.readUTF(); + Log.i(TAG, " + " + packageName); + dataChanged(packageName); + } + } catch (EOFException e) { + // no more data; we're done + } catch (Exception e) { + // can't read it or other error; just skip it + } finally { + // close/delete the file + f.delete(); + } + } + } + } + } + // ----- Track installation/removal of packages ----- BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { @@ -198,6 +252,8 @@ class BackupManagerService extends IBackupManager.Stub { switch (msg.what) { case MSG_RUN_BACKUP: // snapshot the pending-backup set and work on that + File oldJournal = mJournal; + RandomAccessFile oldJournalStream = mJournalStream; synchronized (mQueueLock) { if (mBackupQueue == null) { mBackupQueue = new ArrayList<BackupRequest>(); @@ -207,10 +263,22 @@ class BackupManagerService extends IBackupManager.Stub { mPendingBackups = new HashMap<ApplicationInfo,BackupRequest>(); } // !!! TODO: start a new backup-queue journal file too - // WARNING: If we crash after this line, anything in mPendingBackups will - // be lost. FIX THIS. + if (mJournalStream != null) { + try { + mJournalStream.close(); + } catch (IOException e) { + // don't need to do anything + } + makeJournalLocked(); + } + + // At this point, we have started a new journal file, and the old + // file identity is being passed to the backup processing thread. + // When it completes successfully, that old journal file will be + // deleted. If we crash prior to that, the old journal is parsed + // at next boot and the journaled requests fulfilled. } - (new PerformBackupThread(mTransportId, mBackupQueue)).run(); + (new PerformBackupThread(mTransportId, mBackupQueue, oldJournal)).run(); break; case MSG_RUN_FULL_BACKUP: @@ -433,10 +501,13 @@ class BackupManagerService extends IBackupManager.Stub { private static final String TAG = "PerformBackupThread"; int mTransport; ArrayList<BackupRequest> mQueue; + File mJournal; - public PerformBackupThread(int transportId, ArrayList<BackupRequest> queue) { + public PerformBackupThread(int transportId, ArrayList<BackupRequest> queue, + File journal) { mTransport = transportId; mQueue = queue; + mJournal = journal; } @Override @@ -468,6 +539,10 @@ class BackupManagerService extends IBackupManager.Stub { Log.e(TAG, "Error ending transport"); e.printStackTrace(); } + + if (!mJournal.delete()) { + Log.e(TAG, "Unable to remove backup journal file " + mJournal.getAbsolutePath()); + } } private void doQueuedBackups(IBackupTransport transport) { @@ -782,7 +857,9 @@ class BackupManagerService extends IBackupManager.Stub { // one already there, then overwrite it, but no harm done. BackupRequest req = new BackupRequest(app, false); mPendingBackups.put(app, req); - // !!! TODO: write to the pending-backup journal file in case of crash + + // Journal this request in case of crash + writeToJournalLocked(packageName); } } @@ -804,6 +881,18 @@ class BackupManagerService extends IBackupManager.Stub { } } + private void writeToJournalLocked(String str) { + if (mJournalStream != null) { + try { + mJournalStream.writeUTF(str); + } catch (IOException e) { + Log.e(TAG, "Error writing to backup journal"); + mJournalStream = null; + mJournal = null; + } + } + } + // Schedule a backup pass for a given package. This method will schedule a // full backup even for apps that do not declare an android:backupAgent, so // use with care. diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 6a81178..07d6b6f 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -17,7 +17,6 @@ package com.android.server.am; import com.android.internal.os.BatteryStatsImpl; -import com.android.internal.os.RuntimeInit; import com.android.server.IntentResolver; import com.android.server.ProcessMap; import com.android.server.ProcessStats; @@ -2761,7 +2760,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // instance of the activity so a new fresh one can be started. if (ret.launchMode == ActivityInfo.LAUNCH_MULTIPLE) { if (!ret.finishing) { - int index = indexOfTokenLocked(ret, false); + int index = indexOfTokenLocked(ret); if (index >= 0) { finishActivityLocked(ret, 0, Activity.RESULT_CANCELED, null, "clear"); @@ -2854,7 +2853,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen HistoryRecord sourceRecord = null; HistoryRecord resultRecord = null; if (resultTo != null) { - int index = indexOfTokenLocked(resultTo, false); + int index = indexOfTokenLocked(resultTo); if (DEBUG_RESULTS) Log.v( TAG, "Sending result to " + resultTo + " (index " + index + ")"); if (index >= 0) { @@ -3420,7 +3419,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } synchronized (this) { - int index = indexOfTokenLocked(callingActivity, false); + int index = indexOfTokenLocked(callingActivity); if (index < 0) { return false; } @@ -3570,7 +3569,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen public void setRequestedOrientation(IBinder token, int requestedOrientation) { synchronized (this) { - int index = indexOfTokenLocked(token, false); + int index = indexOfTokenLocked(token); if (index < 0) { return; } @@ -3592,7 +3591,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen public int getRequestedOrientation(IBinder token) { synchronized (this) { - int index = indexOfTokenLocked(token, false); + int index = indexOfTokenLocked(token); if (index < 0) { return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; } @@ -3648,7 +3647,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen TAG, "Finishing activity: token=" + token + ", result=" + resultCode + ", data=" + resultData); - int index = indexOfTokenLocked(token, false); + int index = indexOfTokenLocked(token); if (index < 0) { return false; } @@ -3772,7 +3771,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen private final HistoryRecord finishCurrentActivityLocked(HistoryRecord r, int mode) { - final int index = indexOfTokenLocked(r, false); + final int index = indexOfTokenLocked(r); if (index < 0) { return null; } @@ -3897,7 +3896,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen public final void finishSubActivity(IBinder token, String resultWho, int requestCode) { synchronized(this) { - int index = indexOfTokenLocked(token, false); + int index = indexOfTokenLocked(token); if (index < 0) { return; } @@ -4447,7 +4446,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } synchronized(this) { - int index = indexOfTokenLocked(token, true); + int index = indexOfTokenLocked(token); if (index < 0) { return; } @@ -5012,7 +5011,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } // Get the activity record. - int index = indexOfTokenLocked(token, false); + int index = indexOfTokenLocked(token); if (index >= 0) { HistoryRecord r = (HistoryRecord)mHistory.get(index); @@ -5166,7 +5165,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen HistoryRecord r = null; synchronized (this) { - int index = indexOfTokenLocked(token, false); + int index = indexOfTokenLocked(token); if (index >= 0) { r = (HistoryRecord)mHistory.get(index); if (!timeout) { @@ -5197,7 +5196,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen final long origId = Binder.clearCallingIdentity(); synchronized (this) { - int index = indexOfTokenLocked(token, false); + int index = indexOfTokenLocked(token); if (index >= 0) { r = (HistoryRecord)mHistory.get(index); r.thumbnail = thumbnail; @@ -5227,7 +5226,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen synchronized (this) { mHandler.removeMessages(DESTROY_TIMEOUT_MSG, token); - int index = indexOfTokenLocked(token, false); + int index = indexOfTokenLocked(token); if (index >= 0) { HistoryRecord r = (HistoryRecord)mHistory.get(index); if (r.state == ActivityState.DESTROYING) { @@ -5254,7 +5253,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } private HistoryRecord getCallingRecordLocked(IBinder token) { - int index = indexOfTokenLocked(token, true); + int index = indexOfTokenLocked(token); if (index >= 0) { HistoryRecord r = (HistoryRecord)mHistory.get(index); if (r != null) { @@ -5266,7 +5265,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen public ComponentName getActivityClassForToken(IBinder token) { synchronized(this) { - int index = indexOfTokenLocked(token, false); + int index = indexOfTokenLocked(token); if (index >= 0) { HistoryRecord r = (HistoryRecord)mHistory.get(index); return r.intent.getComponent(); @@ -5277,7 +5276,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen public String getPackageForToken(IBinder token) { synchronized(this) { - int index = indexOfTokenLocked(token, false); + int index = indexOfTokenLocked(token); if (index >= 0) { HistoryRecord r = (HistoryRecord)mHistory.get(index); return r.packageName; @@ -5316,7 +5315,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } HistoryRecord activity = null; if (type == INTENT_SENDER_ACTIVITY_RESULT) { - int index = indexOfTokenLocked(token, false); + int index = indexOfTokenLocked(token); if (index < 0) { return null; } @@ -6837,7 +6836,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen synchronized(this) { if (r == null) { - int index = indexOfTokenLocked(token, false); + int index = indexOfTokenLocked(token); if (index < 0) { return; } @@ -8943,7 +8942,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return false; } - private final int indexOfTokenLocked(IBinder token, boolean required) { + private final int indexOfTokenLocked(IBinder token) { int count = mHistory.size(); // convert the token to an entry in the history. @@ -8957,19 +8956,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen break; } } - if (index < 0 && required) { - RuntimeInit.crash(TAG, new InvalidTokenException(token)); - } return index; } - static class InvalidTokenException extends Exception { - InvalidTokenException(IBinder token) { - super("Bad activity token: " + token); - } - } - private final void killServicesLocked(ProcessRecord app, boolean allowRestart) { // Report disconnected services. @@ -9994,7 +9984,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen HistoryRecord activity = null; if (token != null) { - int aindex = indexOfTokenLocked(token, false); + int aindex = indexOfTokenLocked(token); if (aindex < 0) { Log.w(TAG, "Binding with unknown activity: " + token); return 0; diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java index 42f8fac..c922cec 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java @@ -404,6 +404,8 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { mReconnectIntent = null; } + setState(State.DISCONNECTING); + for (DataConnection connBase : dataConnectionList) { CdmaDataConnection conn = (CdmaDataConnection) connBase; @@ -419,24 +421,9 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { stopNetStatPoll(); - /* - * If we've been asked to tear down the connection, - * set the state to DISCONNECTING. However, there's - * a race that can occur if for some reason we were - * already in the IDLE state. In that case, the call - * to conn.disconnect() above will immediately post - * a message to the handler thread that the disconnect - * is done, and if the handler runs before the code - * below does, the handler will have set the state to - * IDLE before the code below runs. If we didn't check - * for that, future calls to trySetupData would fail, - * and we would never get out of the DISCONNECTING state. - */ if (!tearDown) { setState(State.IDLE); phone.notifyDataConnection(reason); - } else if (state != State.IDLE) { - setState(State.DISCONNECTING); } } @@ -811,6 +798,8 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { resetPollStats(); } } else { + // reset reconnect timer + nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS; // in case data setup was attempted when we were on a voice call trySetupData(Phone.REASON_VOICE_CALL_ENDED); } diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java index 270f78b..71af406 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java @@ -636,6 +636,8 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { mReconnectIntent = null; } + setState(State.DISCONNECTING); + for (DataConnection conn : pdpList) { PdpConnection pdp = (PdpConnection) conn; if (tearDown) { @@ -647,25 +649,10 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { } stopNetStatPoll(); - /* - * If we've been asked to tear down the connection, - * set the state to DISCONNECTING. However, there's - * a race that can occur if for some reason we were - * already in the IDLE state. In that case, the call - * to pdp.disconnect() above will immediately post - * a message to the handler thread that the disconnect - * is done, and if the handler runs before the code - * below does, the handler will have set the state to - * IDLE before the code below runs. If we didn't check - * for that, future calls to trySetupData would fail, - * and we would never get out of the DISCONNECTING state. - */ if (!tearDown) { setState(State.IDLE); phone.notifyDataConnection(reason); mActiveApn = null; - } else if (state != State.IDLE) { - setState(State.DISCONNECTING); } } @@ -813,6 +800,8 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { if (state != State.DISCONNECTING) { cleanUpConnection(isConnected, Phone.REASON_APN_CHANGED); if (!isConnected) { + // reset reconnect timer + nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS; trySetupData(Phone.REASON_APN_CHANGED); } } @@ -1406,6 +1395,8 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { resetPollStats(); } } else { + // reset reconnect timer + nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS; // in case data setup was attempted when we were on a voice call trySetupData(Phone.REASON_VOICE_CALL_ENDED); } |
