diff options
Diffstat (limited to 'services/java/com')
3 files changed, 356 insertions, 95 deletions
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java index e3fff81..82ed1e3 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -16,17 +16,16 @@ package com.android.server; -import android.backup.BackupService; -import android.backup.IBackupService; +import android.app.ActivityManagerNative; +import android.app.IActivityManager; +import android.app.IApplicationThread; +import android.app.IBackupAgent; import android.content.BroadcastReceiver; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.ServiceConnection; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.content.pm.ServiceInfo; import android.net.Uri; import android.os.Binder; import android.os.Bundle; @@ -49,6 +48,7 @@ import java.lang.String; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; class BackupManagerService extends IBackupManager.Stub { @@ -62,22 +62,28 @@ class BackupManagerService extends IBackupManager.Stub { private Context mContext; private PackageManager mPackageManager; + private final IActivityManager mActivityManager; private final BackupHandler mBackupHandler = new BackupHandler(); // map UIDs to the set of backup client services within that UID's app set - private SparseArray<HashSet<ServiceInfo>> mBackupParticipants - = new SparseArray<HashSet<ServiceInfo>>(); + private SparseArray<HashSet<ApplicationInfo>> mBackupParticipants + = new SparseArray<HashSet<ApplicationInfo>>(); // set of backup services that have pending changes private class BackupRequest { - public ServiceInfo service; + public ApplicationInfo appInfo; public boolean fullBackup; - BackupRequest(ServiceInfo svc, boolean isFull) { - service = svc; + BackupRequest(ApplicationInfo app, boolean isFull) { + appInfo = app; fullBackup = isFull; } + + public String toString() { + return "BackupRequest{app=" + appInfo + " full=" + fullBackup + "}"; + } } // Backups that we haven't started yet. - private HashMap<ComponentName,BackupRequest> mPendingBackups = new HashMap(); + private HashMap<ApplicationInfo,BackupRequest> mPendingBackups + = new HashMap<ApplicationInfo,BackupRequest>(); // Backups that we have started. These are separate to prevent starvation // if an app keeps re-enqueuing itself. private ArrayList<BackupRequest> mBackupQueue; @@ -89,6 +95,7 @@ class BackupManagerService extends IBackupManager.Stub { public BackupManagerService(Context context) { mContext = context; mPackageManager = context.getPackageManager(); + mActivityManager = ActivityManagerNative.getDefault(); // Set up our bookkeeping mStateDir = new File(Environment.getDataDirectory(), "backup"); @@ -151,7 +158,7 @@ class BackupManagerService extends IBackupManager.Stub { // ----- Run the actual backup process asynchronously ----- - private class BackupHandler extends Handler implements ServiceConnection { + private class BackupHandler extends Handler { public void handleMessage(Message msg) { switch (msg.what) { @@ -163,31 +170,20 @@ class BackupManagerService extends IBackupManager.Stub { for (BackupRequest b: mPendingBackups.values()) { mBackupQueue.add(b); } - mPendingBackups = new HashMap<ComponentName,BackupRequest>(); + 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. } - startOneService(); + startOneAgent(); break; } } - - public void onServiceConnected(ComponentName name, IBinder service) { - Log.d(TAG, "onServiceConnected name=" + name + " service=" + service); - IBackupService bs = IBackupService.Stub.asInterface(service); - processOneBackup(name, bs); - } - - public void onServiceDisconnected(ComponentName name) { - // TODO: handle backup being interrupted - } } - void startOneService() { + void startOneAgent() { // Loop until we find someone to start or the queue empties out. - Intent intent = new Intent(BackupService.SERVICE_ACTION); while (true) { BackupRequest request; synchronized (mQueueLock) { @@ -205,14 +201,19 @@ class BackupManagerService extends IBackupManager.Stub { // Take it off the queue when we're done. } - intent.setClassName(request.service.packageName, request.service.name); - Log.d(TAG, "binding to " + intent); + Log.d(TAG, "starting agent for " + request); + // !!! TODO: need to handle the restore case? + int mode = (request.fullBackup) + ? IApplicationThread.BACKUP_MODE_FULL + : IApplicationThread.BACKUP_MODE_INCREMENTAL; try { - if (mContext.bindService(intent, mBackupHandler, Context.BIND_AUTO_CREATE)) { - Log.d(TAG, "awaiting service object for " + intent); + if (mActivityManager.bindBackupAgent(request.appInfo, mode)) { + Log.d(TAG, "awaiting agent for " + request); // success return; } + } catch (RemoteException e) { + // can't happen; activity manager is local } catch (SecurityException ex) { // Try for the next one. Log.d(TAG, "error in bind", ex); @@ -220,23 +221,23 @@ class BackupManagerService extends IBackupManager.Stub { } } - void processOneBackup(ComponentName name, IBackupService bs) { - try { - Log.d(TAG, "processOneBackup doBackup() on " + name); + void processOneBackup(String packageName, IBackupAgent bs) { + Log.d(TAG, "processOneBackup doBackup() on " + packageName); - BackupRequest request; - synchronized (mQueueLock) { - if (mBackupQueue == null) { - Log.d(TAG, "mBackupQueue is null. WHY?"); - } - request = mBackupQueue.get(0); + BackupRequest request; + synchronized (mQueueLock) { + if (mBackupQueue == null) { + Log.d(TAG, "mBackupQueue is null. WHY?"); } + request = mBackupQueue.get(0); + } + try { // !!! TODO right now these naming schemes limit applications to // one backup service per package - File savedStateName = new File(mStateDir, request.service.packageName); - File backupDataName = new File(mDataDir, request.service.packageName + ".data"); - File newStateName = new File(mStateDir, request.service.packageName + ".new"); + File savedStateName = new File(mStateDir, request.appInfo.packageName); + File backupDataName = new File(mDataDir, request.appInfo.packageName + ".data"); + File newStateName = new File(mStateDir, request.appInfo.packageName + ".new"); // In a full backup, we pass a null ParcelFileDescriptor as // the saved-state "file" @@ -280,7 +281,7 @@ class BackupManagerService extends IBackupManager.Stub { Log.d(TAG, "File not found on backup: "); fnf.printStackTrace(); } catch (RemoteException e) { - Log.d(TAG, "Remote target " + name + " threw during backup:"); + Log.d(TAG, "Remote target " + packageName + " threw during backup:"); e.printStackTrace(); } catch (Exception e) { Log.w(TAG, "Final exception guard in backup: "); @@ -289,37 +290,45 @@ class BackupManagerService extends IBackupManager.Stub { synchronized (mQueueLock) { mBackupQueue.remove(0); } - mContext.unbindService(mBackupHandler); + + if (request != null) { + try { + mActivityManager.unbindBackupAgent(request.appInfo); + } catch (RemoteException e) { + // can't happen + } + } // start the next one - startOneService(); + startOneAgent(); } - // Add the backup services in the given package to our set of known backup participants. - // If 'packageName' is null, adds all backup services in the system. + // Add the backup agents in the given package to our set of known backup participants. + // If 'packageName' is null, adds all backup agents in the whole system. void addPackageParticipantsLocked(String packageName) { - List<ResolveInfo> services = mPackageManager.queryIntentServices( - new Intent(BackupService.SERVICE_ACTION), 0); - addPackageParticipantsLockedInner(packageName, services); + // Look for apps that define the android:backupAgent attribute + List<ApplicationInfo> targetApps = allAgentApps(); + addPackageParticipantsLockedInner(packageName, targetApps); } - private void addPackageParticipantsLockedInner(String packageName, List<ResolveInfo> services) { - for (ResolveInfo ri : services) { - if (packageName == null || ri.serviceInfo.packageName.equals(packageName)) { - int uid = ri.serviceInfo.applicationInfo.uid; - HashSet<ServiceInfo> set = mBackupParticipants.get(uid); + private void addPackageParticipantsLockedInner(String packageName, + List<ApplicationInfo> targetApps) { + if (DEBUG) { + Log.v(TAG, "Adding " + targetApps.size() + " backup participants:"); + for (ApplicationInfo a : targetApps) { + Log.v(TAG, " " + a + " agent=" + a.backupAgentName); + } + } + + for (ApplicationInfo app : targetApps) { + if (packageName == null || app.packageName.equals(packageName)) { + int uid = app.uid; + HashSet<ApplicationInfo> set = mBackupParticipants.get(uid); if (set == null) { - set = new HashSet<ServiceInfo>(); + set = new HashSet<ApplicationInfo>(); mBackupParticipants.put(uid, set); } - if (DEBUG) { - Log.v(TAG, "Adding " + services.size() + " backup participants:"); - for (ResolveInfo svc : services) { - Log.v(TAG, " " + svc + " : " + svc.filter); - } - } - - set.add(ri.serviceInfo); + set.add(app); } } } @@ -327,19 +336,30 @@ class BackupManagerService extends IBackupManager.Stub { // Remove the given package's backup services from our known active set. If // 'packageName' is null, *all* backup services will be removed. void removePackageParticipantsLocked(String packageName) { - List<ResolveInfo> services = mPackageManager.queryIntentServices( - new Intent(BackupService.SERVICE_ACTION), 0); - removePackageParticipantsLockedInner(packageName, services); + List<ApplicationInfo> allApps = null; + if (packageName != null) { + allApps = new ArrayList<ApplicationInfo>(); + try { + ApplicationInfo app = mPackageManager.getApplicationInfo(packageName, 0); + allApps.add(app); + } catch (Exception e) { + // just skip it + } + } else { + // all apps with agents + allApps = allAgentApps(); + } + removePackageParticipantsLockedInner(packageName, allApps); } private void removePackageParticipantsLockedInner(String packageName, - List<ResolveInfo> services) { - for (ResolveInfo ri : services) { - if (packageName == null || ri.serviceInfo.packageName.equals(packageName)) { - int uid = ri.serviceInfo.applicationInfo.uid; - HashSet<ServiceInfo> set = mBackupParticipants.get(uid); + List<ApplicationInfo> agents) { + for (ApplicationInfo app : agents) { + if (packageName == null || app.packageName.equals(packageName)) { + int uid = app.uid; + HashSet<ApplicationInfo> set = mBackupParticipants.get(uid); if (set != null) { - set.remove(ri.serviceInfo); + set.remove(app); if (set.size() == 0) { mBackupParticipants.put(uid, null); } @@ -348,6 +368,21 @@ class BackupManagerService extends IBackupManager.Stub { } } + // Returns the set of all applications that define an android:backupAgent attribute + private List<ApplicationInfo> allAgentApps() { + List<ApplicationInfo> allApps = mPackageManager.getInstalledApplications(0); + int N = allApps.size(); + if (N > 0) { + for (int a = N-1; a >= 0; a--) { + ApplicationInfo app = allApps.get(a); + if (app.backupAgentName == null) { + allApps.remove(a); + } + } + } + return allApps; + } + // Reset the given package's known backup participants. Unlike add/remove, the update // action cannot be passed a null package name. void updatePackageParticipantsLocked(String packageName) { @@ -357,10 +392,9 @@ class BackupManagerService extends IBackupManager.Stub { } // brute force but small code size - List<ResolveInfo> services = mPackageManager.queryIntentServices( - new Intent(BackupService.SERVICE_ACTION), 0); - removePackageParticipantsLockedInner(packageName, services); - addPackageParticipantsLockedInner(packageName, services); + List<ApplicationInfo> allApps = allAgentApps(); + removePackageParticipantsLockedInner(packageName, allApps); + addPackageParticipantsLockedInner(packageName, allApps); } // ----- IBackupManager binder interface ----- @@ -372,24 +406,29 @@ class BackupManagerService extends IBackupManager.Stub { Log.d(TAG, "dataChanged packageName=" + packageName); - HashSet<ServiceInfo> targets = mBackupParticipants.get(Binder.getCallingUid()); - Log.d(TAG, "targets=" + targets); + HashSet<ApplicationInfo> targets = mBackupParticipants.get(Binder.getCallingUid()); if (targets != null) { synchronized (mQueueLock) { // Note that this client has made data changes that need to be backed up - for (ServiceInfo service : targets) { + for (ApplicationInfo app : targets) { // validate the caller-supplied package name against the known set of // packages associated with this uid - if (service.packageName.equals(packageName)) { + if (app.packageName.equals(packageName)) { // Add the caller to the set of pending backups. If there is // one already there, then overwrite it, but no harm done. - mPendingBackups.put(new ComponentName(service.packageName, service.name), - new BackupRequest(service, true)); + BackupRequest req = new BackupRequest(app, false); + mPendingBackups.put(app, req); // !!! TODO: write to the pending-backup journal file in case of crash } } - Log.d(TAG, "Scheduling backup for " + mPendingBackups.size() + " participants"); + if (DEBUG) { + int numKeys = mPendingBackups.size(); + Log.d(TAG, "Scheduling backup for " + numKeys + " participants:"); + for (BackupRequest b : mPendingBackups.values()) { + Log.d(TAG, " + " + b + " agent=" + b.appInfo.backupAgentName); + } + } // Schedule a backup pass in a few minutes. As backup-eligible data // keeps changing, continue to defer the backup pass until things // settle down, to avoid extra overhead. @@ -402,22 +441,35 @@ class BackupManagerService extends IBackupManager.Stub { // that uid or package itself. public void scheduleFullBackup(String packageName) throws RemoteException { // !!! TODO: protect with a signature-or-system permission? - HashSet<ServiceInfo> targets = new HashSet<ServiceInfo>(); synchronized (mQueueLock) { int numKeys = mBackupParticipants.size(); for (int index = 0; index < numKeys; index++) { int uid = mBackupParticipants.keyAt(index); - HashSet<ServiceInfo> servicesAtUid = mBackupParticipants.get(uid); - for (ServiceInfo service: servicesAtUid) { - if (service.packageName.equals(packageName)) { - mPendingBackups.put(new ComponentName(service.packageName, service.name), - new BackupRequest(service, true)); + HashSet<ApplicationInfo> servicesAtUid = mBackupParticipants.get(uid); + for (ApplicationInfo app: servicesAtUid) { + if (app.packageName.equals(packageName)) { + mPendingBackups.put(app, new BackupRequest(app, true)); } } } } } + // Callback: a requested backup agent has been instantiated + public void agentConnected(String packageName, IBinder agentBinder) { + Log.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder); + IBackupAgent bs = IBackupAgent.Stub.asInterface(agentBinder); + processOneBackup(packageName, bs); + } + + // Callback: a backup agent has failed to come up, or has unexpectedly quit. + // If the agent failed to come up in the first place, the agentBinder argument + // will be null. + public void agentDisconnected(String packageName) { + // TODO: handle backup being interrupted + } + + @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { @@ -428,12 +480,18 @@ class BackupManagerService extends IBackupManager.Stub { int uid = mBackupParticipants.keyAt(i); pw.print(" uid: "); pw.println(uid); - HashSet<ServiceInfo> services = mBackupParticipants.valueAt(i); - for (ServiceInfo s: services) { + HashSet<ApplicationInfo> participants = mBackupParticipants.valueAt(i); + for (ApplicationInfo app: participants) { pw.print(" "); - pw.println(s.toString()); + pw.println(app.toString()); } } + pw.println("Pending:"); + Iterator<BackupRequest> br = mPendingBackups.values().iterator(); + while (br.hasNext()) { + pw.print(" "); + pw.println(br); + } } } } 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 |