diff options
Diffstat (limited to 'services/java/com/android/server/am')
-rw-r--r-- | services/java/com/android/server/am/ActivityManagerService.java | 148 | ||||
-rw-r--r-- | services/java/com/android/server/am/BackupRecord.java | 57 |
2 files changed, 204 insertions, 1 deletions
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index d1c40b4..3b26cb7 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -42,6 +42,7 @@ import android.app.IThumbnailReceiver; import android.app.Instrumentation; import android.app.PendingIntent; import android.app.ResultInfo; +import android.backup.IBackupManager; import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.ContentResolver; @@ -131,6 +132,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen static final boolean DEBUG_PROCESSES = localLOGV || false; static final boolean DEBUG_USER_LEAVING = localLOGV || false; static final boolean DEBUG_RESULTS = localLOGV || false; + static final boolean DEBUG_BACKUP = localLOGV || true; static final boolean VALIDATE_TOKENS = false; static final boolean SHOW_ACTIVITY_START_TIME = true; @@ -633,6 +635,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen = new ArrayList<ServiceRecord>(); /** + * Backup/restore process management + */ + String mBackupAppName = null; + BackupRecord mBackupTarget = null; + + /** * List of PendingThumbnailsRecord objects of clients who are still * waiting to receive all of the thumbnails for a task. */ @@ -4669,6 +4677,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen mPendingBroadcast = null; scheduleBroadcastsLocked(); } + if (mBackupTarget != null && mBackupTarget.app.pid == pid) { + Log.w(TAG, "Unattached app died before backup, skipping"); + try { + IBackupManager bm = IBackupManager.Stub.asInterface( + ServiceManager.getService(Context.BACKUP_SERVICE)); + bm.agentDisconnected(app.info.packageName); + } catch (RemoteException e) { + // Can't happen; the backup manager is local + } + } } else { Log.w(TAG, "Spurious process start timeout - pid not known for " + app); } @@ -4757,11 +4775,17 @@ public final class ActivityManagerService extends ActivityManagerNative implemen mWaitForDebugger = mOrigWaitForDebugger; } } + // If the app is being launched for restore or full backup, set it up specially + boolean isRestrictedBackupMode = false; + if (mBackupTarget != null && mBackupAppName.equals(processName)) { + isRestrictedBackupMode = (mBackupTarget.backupMode == BackupRecord.RESTORE) + || (mBackupTarget.backupMode == BackupRecord.BACKUP_FULL); + } thread.bindApplication(processName, app.instrumentationInfo != null ? app.instrumentationInfo : app.info, providers, app.instrumentationClass, app.instrumentationProfileFile, app.instrumentationArguments, app.instrumentationWatcher, testMode, - mConfiguration, getCommonServicesLocked()); + isRestrictedBackupMode, mConfiguration, getCommonServicesLocked()); updateLRUListLocked(app, false); app.lastRequestedGc = SystemClock.uptimeMillis(); } catch (Exception e) { @@ -4842,6 +4866,17 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } + // Check whether the next backup agent is in this process... + if (!badApp && mBackupTarget != null && mBackupTarget.appInfo.uid == app.info.uid) { + if (DEBUG_BACKUP) Log.v(TAG, "New app is backup target, launching agent for " + app); + try { + thread.scheduleCreateBackupAgent(mBackupTarget.appInfo, mBackupTarget.backupMode); + } catch (Exception e) { + Log.w(TAG, "Exception scheduling backup agent creation: "); + e.printStackTrace(); + } + } + if (badApp) { // todo: Also need to kill application to deal with all // kinds of exceptions. @@ -9118,6 +9153,18 @@ public final class ActivityManagerService extends ActivityManagerNative implemen app.receivers.clear(); } + // If the app is undergoing backup, tell the backup manager about it + if (mBackupTarget != null && app.pid == mBackupTarget.app.pid) { + if (DEBUG_BACKUP) Log.d(TAG, "App " + mBackupTarget.appInfo + " died during backup"); + try { + IBackupManager bm = IBackupManager.Stub.asInterface( + ServiceManager.getService(Context.BACKUP_SERVICE)); + bm.agentDisconnected(app.info.packageName); + } catch (RemoteException e) { + // can't happen; backup manager is local + } + } + // If the caller is restarting this app, then leave it in its // current lists and let the caller take care of it. if (restarting) { @@ -10234,6 +10281,105 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } // ========================================================= + // BACKUP AND RESTORE + // ========================================================= + + // Cause the target app to be launched if necessary and its backup agent + // instantiated. The backup agent will invoke backupAgentCreated() on the + // activity manager to announce its creation. + public boolean bindBackupAgent(ApplicationInfo app, int backupMode) { + if (DEBUG_BACKUP) Log.v(TAG, "startBackupAgent: app=" + app + " mode=" + backupMode); + enforceCallingPermission("android.permission.BACKUP", "startBackupAgent"); + + synchronized(this) { + // !!! TODO: currently no check here that we're already bound + BatteryStatsImpl.Uid.Pkg.Serv ss = null; + BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics(); + synchronized (stats) { + ss = stats.getServiceStatsLocked(app.uid, app.packageName, app.name); + } + + BackupRecord r = new BackupRecord(ss, app, backupMode); + ComponentName hostingName = new ComponentName(app.packageName, app.backupAgentName); + // startProcessLocked() returns existing proc's record if it's already running + ProcessRecord proc = startProcessLocked(app.processName, app, + false, 0, "backup", hostingName); + if (proc == null) { + Log.e(TAG, "Unable to start backup agent process " + r); + return false; + } + + r.app = proc; + mBackupTarget = r; + mBackupAppName = app.packageName; + + // If the process is already attached, schedule the creation of the backup agent now. + // If it is not yet live, this will be done when it attaches to the framework. + if (proc.thread != null) { + if (DEBUG_BACKUP) Log.v(TAG, "Agent proc already running: " + proc); + try { + proc.thread.scheduleCreateBackupAgent(app, backupMode); + } catch (RemoteException e) { + // !!! TODO: notify the backup manager that we crashed, or rely on + // death notices, or...? + } + } else { + if (DEBUG_BACKUP) Log.v(TAG, "Agent proc not running, waiting for attach"); + } + // Invariants: at this point, the target app process exists and the application + // is either already running or in the process of coming up. mBackupTarget and + // mBackupAppName describe the app, so that when it binds back to the AM we + // know that it's scheduled for a backup-agent operation. + } + + return true; + } + + // A backup agent has just come up + public void backupAgentCreated(String agentPackageName, IBinder agent) { + if (DEBUG_BACKUP) Log.v(TAG, "backupAgentCreated: " + agentPackageName + + " = " + agent); + + synchronized(this) { + if (!agentPackageName.equals(mBackupAppName)) { + Log.e(TAG, "Backup agent created for " + agentPackageName + " but not requested!"); + return; + } + + try { + IBackupManager bm = IBackupManager.Stub.asInterface( + ServiceManager.getService(Context.BACKUP_SERVICE)); + bm.agentConnected(agentPackageName, agent); + } catch (RemoteException e) { + // can't happen; the backup manager service is local + } catch (Exception e) { + Log.w(TAG, "Exception trying to deliver BackupAgent binding: "); + e.printStackTrace(); + } + } + } + + // done with this agent + public void unbindBackupAgent(ApplicationInfo appInfo) { + if (DEBUG_BACKUP) Log.v(TAG, "unbindBackupAgent: " + appInfo); + + synchronized(this) { + if (!mBackupAppName.equals(appInfo.packageName)) { + Log.e(TAG, "Unbind of " + appInfo + " but is not the current backup target"); + return; + } + + try { + mBackupTarget.app.thread.scheduleDestroyBackupAgent(appInfo); + } catch (Exception e) { + Log.e(TAG, "Exception when unbinding backup agent:"); + e.printStackTrace(); + } + mBackupTarget = null; + mBackupAppName = null; + } + } + // ========================================================= // BROADCASTS // ========================================================= diff --git a/services/java/com/android/server/am/BackupRecord.java b/services/java/com/android/server/am/BackupRecord.java new file mode 100644 index 0000000..5ac8e0d --- /dev/null +++ b/services/java/com/android/server/am/BackupRecord.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.am; + +import com.android.internal.os.BatteryStatsImpl; + +import android.content.pm.ApplicationInfo; + +/** @hide */ +class BackupRecord { + // backup/restore modes + public static final int BACKUP_NORMAL = 0; + public static final int BACKUP_FULL = 1; + public static final int RESTORE = 2; + + final BatteryStatsImpl.Uid.Pkg.Serv stats; + String stringName; // cached toString() output + final ApplicationInfo appInfo; // information about BackupAgent's app + final int backupMode; // full backup / incremental / restore + ProcessRecord app; // where this agent is running or null + + // ----- Implementation ----- + + BackupRecord(BatteryStatsImpl.Uid.Pkg.Serv _agentStats, ApplicationInfo _appInfo, + int _backupMode) { + stats = _agentStats; + appInfo = _appInfo; + backupMode = _backupMode; + } + + public String toString() { + if (stringName != null) { + return stringName; + } + StringBuilder sb = new StringBuilder(128); + sb.append("BackupRecord{") + .append(Integer.toHexString(System.identityHashCode(this))) + .append(' ').append(appInfo.packageName) + .append(' ').append(appInfo.name) + .append(' ').append(appInfo.backupAgentName).append('}'); + return stringName = sb.toString(); + } +}
\ No newline at end of file |