summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--services/java/com/android/server/BackupManagerService.java99
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java50
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java19
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java21
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);
}