BackupManagerServicepublic class BackupManagerService extends Object
Fields Summary |
---|
private static final String | TAG | private static final boolean | DEBUG | private static final boolean | MORE_DEBUG | private static final boolean | DEBUG_SCHEDULING | static final String | KEY_WIDGET_STATE | static final String | PBKDF_CURRENT | static final String | PBKDF_FALLBACK | static final String | BACKUP_MANIFEST_FILENAME | static final int | BACKUP_MANIFEST_VERSION | static final int | BACKUP_FILE_VERSION | static final String | BACKUP_FILE_HEADER_MAGIC | static final int | BACKUP_PW_FILE_VERSION | static final String | BACKUP_METADATA_FILENAME | static final int | BACKUP_METADATA_VERSION | static final int | BACKUP_WIDGET_METADATA_TOKEN | static final boolean | COMPRESS_FULL_BACKUPS | static final String | SETTINGS_PACKAGE | static final String | SHARED_BACKUP_AGENT_PACKAGE | static final String | SERVICE_ACTION_TRANSPORT_HOST | private static final long | BACKUP_INTERVAL | private static final int | FUZZ_MILLIS | private static final long | FIRST_BACKUP_INTERVAL | private static final long | TRANSPORT_RETRY_INTERVAL | private static final String | RUN_BACKUP_ACTION | private static final String | RUN_INITIALIZE_ACTION | private static final String | RUN_CLEAR_ACTION | private static final int | MSG_RUN_BACKUP | private static final int | MSG_RUN_ADB_BACKUP | private static final int | MSG_RUN_RESTORE | private static final int | MSG_RUN_CLEAR | private static final int | MSG_RUN_INITIALIZE | private static final int | MSG_RUN_GET_RESTORE_SETS | private static final int | MSG_TIMEOUT | private static final int | MSG_RESTORE_TIMEOUT | private static final int | MSG_FULL_CONFIRMATION_TIMEOUT | private static final int | MSG_RUN_ADB_RESTORE | private static final int | MSG_RETRY_INIT | private static final int | MSG_RETRY_CLEAR | private static final int | MSG_WIDGET_BROADCAST | private static final int | MSG_RUN_FULL_TRANSPORT_BACKUP | static final int | MSG_BACKUP_RESTORE_STEP | static final int | MSG_OP_COMPLETE | static final long | TIMEOUT_INTERVAL | static final long | TIMEOUT_BACKUP_INTERVAL | static final long | TIMEOUT_FULL_BACKUP_INTERVAL | static final long | TIMEOUT_SHARED_BACKUP_INTERVAL | static final long | TIMEOUT_RESTORE_INTERVAL | static final long | TIMEOUT_RESTORE_FINISHED_INTERVAL | static final long | TIMEOUT_FULL_CONFIRMATION | static final long | MIN_FULL_BACKUP_INTERVAL | android.content.Context | mContext | private android.content.pm.PackageManager | mPackageManager | android.content.pm.IPackageManager | mPackageManagerBinder | private android.app.IActivityManager | mActivityManager | private android.os.PowerManager | mPowerManager | private android.app.AlarmManager | mAlarmManager | private android.os.storage.IMountService | mMountService | android.app.backup.IBackupManager | mBackupManagerBinder | boolean | mEnabled | boolean | mProvisioned | boolean | mAutoRestore | PowerManager.WakeLock | mWakelock | android.os.HandlerThread | mHandlerThread | BackupHandler | mBackupHandler | android.app.PendingIntent | mRunBackupIntent | android.app.PendingIntent | mRunInitIntent | android.content.BroadcastReceiver | mRunBackupReceiver | android.content.BroadcastReceiver | mRunInitReceiver | final android.util.SparseArray | mBackupParticipants | HashMap | mPendingBackups | static final String | PACKAGE_MANAGER_SENTINEL | final Object | mQueueLock | final Object | mAgentConnectLock | android.app.IBackupAgent | mConnectedAgent | volatile boolean | mBackupRunning | volatile boolean | mConnecting | volatile long | mLastBackupPass | volatile long | mNextBackupPass | static final boolean | DEBUG_BACKUP_TRACE | final List | mBackupTrace | final Object | mClearDataLock | volatile boolean | mClearingData | final android.content.Intent | mTransportServiceIntent | final android.util.ArrayMap | mTransportNames | final android.util.ArrayMap | mTransports | final android.util.ArrayMap | mTransportConnections | String | mCurrentTransport | ActiveRestoreSession | mActiveRestoreSession | android.database.ContentObserver | mProvisionedObserver | static Trampoline | sInstance | static final int | OP_PENDING | static final int | OP_ACKNOWLEDGED | static final int | OP_TIMEOUT | final android.util.SparseArray | mCurrentOperations | final Object | mCurrentOpLock | final Random | mTokenGenerator | final android.util.SparseArray | mFullConfirmations | File | mBaseStateDir | File | mDataDir | File | mJournalDir | File | mJournal | private final SecureRandom | mRng | private String | mPasswordHash | private File | mPasswordHashFile | private int | mPasswordVersion | private File | mPasswordVersionFile | private byte[] | mPasswordSalt | static final int | PBKDF2_HASH_ROUNDS | static final int | PBKDF2_KEY_SIZE | static final int | PBKDF2_SALT_SIZE | static final String | ENCRYPTION_ALGORITHM_NAME | private File | mEverStored | HashSet | mEverStoredApps | static final int | CURRENT_ANCESTRAL_RECORD_VERSION | File | mTokenFile | Set | mAncestralPackages | long | mAncestralToken | long | mCurrentToken | static final String | INIT_SENTINEL_FILE_NAME | HashSet | mPendingInits | static final int | SCHEDULE_FILE_VERSION | File | mFullBackupScheduleFile | PerformFullTransportBackupTask | mRunningFullBackupTask | ArrayList | mFullBackupQueue | Runnable | mFullBackupScheduleWriter | android.content.BroadcastReceiver | mBroadcastReceiver |
Constructors Summary |
---|
public BackupManagerService(android.content.Context context, Trampoline parent)
mContext = context;
mPackageManager = context.getPackageManager();
mPackageManagerBinder = AppGlobals.getPackageManager();
mActivityManager = ActivityManagerNative.getDefault();
mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));
mBackupManagerBinder = Trampoline.asInterface(parent.asBinder());
// spin up the backup/restore handler thread
mHandlerThread = new HandlerThread("backup", Process.THREAD_PRIORITY_BACKGROUND);
mHandlerThread.start();
mBackupHandler = new BackupHandler(mHandlerThread.getLooper());
// Set up our bookkeeping
final ContentResolver resolver = context.getContentResolver();
mProvisioned = Settings.Global.getInt(resolver,
Settings.Global.DEVICE_PROVISIONED, 0) != 0;
mAutoRestore = Settings.Secure.getInt(resolver,
Settings.Secure.BACKUP_AUTO_RESTORE, 1) != 0;
mProvisionedObserver = new ProvisionedObserver(mBackupHandler);
resolver.registerContentObserver(
Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
false, mProvisionedObserver);
// If Encrypted file systems is enabled or disabled, this call will return the
// correct directory.
mBaseStateDir = new File(Environment.getSecureDataDirectory(), "backup");
mBaseStateDir.mkdirs();
if (!SELinux.restorecon(mBaseStateDir)) {
Slog.e(TAG, "SELinux restorecon failed on " + mBaseStateDir);
}
mDataDir = Environment.getDownloadCacheDirectory();
mPasswordVersion = 1; // unless we hear otherwise
mPasswordVersionFile = new File(mBaseStateDir, "pwversion");
if (mPasswordVersionFile.exists()) {
FileInputStream fin = null;
DataInputStream in = null;
try {
fin = new FileInputStream(mPasswordVersionFile);
in = new DataInputStream(fin);
mPasswordVersion = in.readInt();
} catch (IOException e) {
Slog.e(TAG, "Unable to read backup pw version");
} finally {
try {
if (in != null) in.close();
if (fin != null) fin.close();
} catch (IOException e) {
Slog.w(TAG, "Error closing pw version files");
}
}
}
mPasswordHashFile = new File(mBaseStateDir, "pwhash");
if (mPasswordHashFile.exists()) {
FileInputStream fin = null;
DataInputStream in = null;
try {
fin = new FileInputStream(mPasswordHashFile);
in = new DataInputStream(new BufferedInputStream(fin));
// integer length of the salt array, followed by the salt,
// then the hex pw hash string
int saltLen = in.readInt();
byte[] salt = new byte[saltLen];
in.readFully(salt);
mPasswordHash = in.readUTF();
mPasswordSalt = salt;
} catch (IOException e) {
Slog.e(TAG, "Unable to read saved backup pw hash");
} finally {
try {
if (in != null) in.close();
if (fin != null) fin.close();
} catch (IOException e) {
Slog.w(TAG, "Unable to close streams");
}
}
}
// Alarm receivers for scheduled backups & initialization operations
mRunBackupReceiver = new RunBackupReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(RUN_BACKUP_ACTION);
context.registerReceiver(mRunBackupReceiver, filter,
android.Manifest.permission.BACKUP, null);
mRunInitReceiver = new RunInitializeReceiver();
filter = new IntentFilter();
filter.addAction(RUN_INITIALIZE_ACTION);
context.registerReceiver(mRunInitReceiver, filter,
android.Manifest.permission.BACKUP, null);
Intent backupIntent = new Intent(RUN_BACKUP_ACTION);
backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
mRunBackupIntent = PendingIntent.getBroadcast(context, MSG_RUN_BACKUP, backupIntent, 0);
Intent initIntent = new Intent(RUN_INITIALIZE_ACTION);
backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
mRunInitIntent = PendingIntent.getBroadcast(context, MSG_RUN_INITIALIZE, initIntent, 0);
// Set up the backup-request journaling
mJournalDir = new File(mBaseStateDir, "pending");
mJournalDir.mkdirs(); // creates mBaseStateDir along the way
mJournal = null; // will be created on first use
// Set up the various sorts of package tracking we do
mFullBackupScheduleFile = new File(mBaseStateDir, "fb-schedule");
initPackageTracking();
// Build our mapping of uid to backup client services. This implicitly
// schedules a backup pass on the Package Manager metadata the first
// time anything needs to be backed up.
synchronized (mBackupParticipants) {
addPackageParticipantsLocked(null);
}
// Set up our transport options and initialize the default transport
// TODO: Don't create transports that we don't need to?
mCurrentTransport = Settings.Secure.getString(context.getContentResolver(),
Settings.Secure.BACKUP_TRANSPORT);
if ("".equals(mCurrentTransport)) {
mCurrentTransport = null;
}
if (DEBUG) Slog.v(TAG, "Starting with transport " + mCurrentTransport);
// Find all transport hosts and bind to their services
List<ResolveInfo> hosts = mPackageManager.queryIntentServicesAsUser(
mTransportServiceIntent, 0, UserHandle.USER_OWNER);
if (DEBUG) {
Slog.v(TAG, "Found transports: " + ((hosts == null) ? "null" : hosts.size()));
}
if (hosts != null) {
for (int i = 0; i < hosts.size(); i++) {
final ServiceInfo transport = hosts.get(i).serviceInfo;
if (MORE_DEBUG) {
Slog.v(TAG, " " + transport.packageName + "/" + transport.name);
}
tryBindTransport(transport);
}
}
// Now that we know about valid backup participants, parse any
// leftover journal files into the pending backup set
parseLeftoverJournals();
// Power management
mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*");
|
Methods Summary |
---|
public void | acknowledgeFullBackupOrRestore(int token, boolean allow, java.lang.String curPassword, java.lang.String encPpassword, android.app.backup.IFullBackupRestoreObserver observer)
if (DEBUG) Slog.d(TAG, "acknowledgeFullBackupOrRestore : token=" + token
+ " allow=" + allow);
// TODO: possibly require not just this signature-only permission, but even
// require that the specific designated confirmation-UI app uid is the caller?
mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "acknowledgeFullBackupOrRestore");
long oldId = Binder.clearCallingIdentity();
try {
FullParams params;
synchronized (mFullConfirmations) {
params = mFullConfirmations.get(token);
if (params != null) {
mBackupHandler.removeMessages(MSG_FULL_CONFIRMATION_TIMEOUT, params);
mFullConfirmations.delete(token);
if (allow) {
final int verb = params instanceof FullBackupParams
? MSG_RUN_ADB_BACKUP
: MSG_RUN_ADB_RESTORE;
params.observer = observer;
params.curPassword = curPassword;
params.encryptPassword = encPpassword;
if (DEBUG) Slog.d(TAG, "Sending conf message with verb " + verb);
mWakelock.acquire();
Message msg = mBackupHandler.obtainMessage(verb, params);
mBackupHandler.sendMessage(msg);
} else {
Slog.w(TAG, "User rejected full backup/restore operation");
// indicate completion without having actually transferred any data
signalFullBackupRestoreCompletion(params);
}
} else {
Slog.w(TAG, "Attempted to ack full backup/restore with invalid token");
}
}
} finally {
Binder.restoreCallingIdentity(oldId);
}
| void | addBackupTrace(java.lang.String s)
if (DEBUG_BACKUP_TRACE) {
synchronized (mBackupTrace) {
mBackupTrace.add(s);
}
}
| void | addPackageParticipantsLocked(java.lang.String[] packageNames)
// Look for apps that define the android:backupAgent attribute
List<PackageInfo> targetApps = allAgentPackages();
if (packageNames != null) {
if (MORE_DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: #" + packageNames.length);
for (String packageName : packageNames) {
addPackageParticipantsLockedInner(packageName, targetApps);
}
} else {
if (MORE_DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: all");
addPackageParticipantsLockedInner(null, targetApps);
}
| private void | addPackageParticipantsLockedInner(java.lang.String packageName, java.util.List targetPkgs)
if (MORE_DEBUG) {
Slog.v(TAG, "Examining " + packageName + " for backup agent");
}
for (PackageInfo pkg : targetPkgs) {
if (packageName == null || pkg.packageName.equals(packageName)) {
int uid = pkg.applicationInfo.uid;
HashSet<String> set = mBackupParticipants.get(uid);
if (set == null) {
set = new HashSet<String>();
mBackupParticipants.put(uid, set);
}
set.add(pkg.packageName);
if (MORE_DEBUG) Slog.v(TAG, "Agent found; added");
// Schedule a backup for it on general principles
if (MORE_DEBUG) Slog.i(TAG, "Scheduling backup for new app " + pkg.packageName);
dataChangedImpl(pkg.packageName);
}
}
| public void | agentConnected(java.lang.String packageName, android.os.IBinder agentBinder)
synchronized(mAgentConnectLock) {
if (Binder.getCallingUid() == Process.SYSTEM_UID) {
Slog.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder);
IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder);
mConnectedAgent = agent;
mConnecting = false;
} else {
Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
+ " claiming agent connected");
}
mAgentConnectLock.notifyAll();
}
| public void | agentDisconnected(java.lang.String packageName)
// TODO: handle backup being interrupted
synchronized(mAgentConnectLock) {
if (Binder.getCallingUid() == Process.SYSTEM_UID) {
mConnectedAgent = null;
mConnecting = false;
} else {
Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
+ " claiming agent disconnected");
}
mAgentConnectLock.notifyAll();
}
| java.util.List | allAgentPackages()
// !!! TODO: cache this and regenerate only when necessary
int flags = PackageManager.GET_SIGNATURES;
List<PackageInfo> packages = mPackageManager.getInstalledPackages(flags);
int N = packages.size();
for (int a = N-1; a >= 0; a--) {
PackageInfo pkg = packages.get(a);
try {
ApplicationInfo app = pkg.applicationInfo;
if (((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0)
|| app.backupAgentName == null) {
packages.remove(a);
}
else {
// we will need the shared library path, so look that up and store it here.
// This is used implicitly when we pass the PackageInfo object off to
// the Activity Manager to launch the app for backup/restore purposes.
app = mPackageManager.getApplicationInfo(pkg.packageName,
PackageManager.GET_SHARED_LIBRARY_FILES);
pkg.applicationInfo.sharedLibraryFiles = app.sharedLibraryFiles;
}
} catch (NameNotFoundException e) {
packages.remove(a);
}
}
return packages;
| public static boolean | appGetsFullBackup(android.content.pm.PackageInfo pkg)
if (pkg.applicationInfo.backupAgentName != null) {
// If it has an agent, it gets full backups only if it says so
return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_FULL_BACKUP_ONLY) != 0;
}
// No agent means we do full backups for it
return true;
| public static boolean | appIsEligibleForBackup(android.content.pm.ApplicationInfo app)
// 1. their manifest states android:allowBackup="false"
if ((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) {
return false;
}
// 2. they run as a system-level uid but do not supply their own backup agent
if ((app.uid < Process.FIRST_APPLICATION_UID) && (app.backupAgentName == null)) {
return false;
}
// 3. it is the special shared-storage backup package used for 'adb backup'
if (app.packageName.equals(BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE)) {
return false;
}
return true;
| public void | backupNow()
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "backupNow");
if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass");
synchronized (mQueueLock) {
// Because the alarms we are using can jitter, and we want an *immediate*
// backup pass to happen, we restart the timer beginning with "next time,"
// then manually fire the backup trigger intent ourselves.
startBackupAlarmsLocked(BACKUP_INTERVAL);
try {
mRunBackupIntent.send();
} catch (PendingIntent.CanceledException e) {
// should never happen
Slog.e(TAG, "run-backup intent cancelled!");
}
}
| private boolean | backupPasswordMatches(java.lang.String currentPw)
if (hasBackupPassword()) {
final boolean pbkdf2Fallback = (mPasswordVersion < BACKUP_PW_FILE_VERSION);
if (!passwordMatchesSaved(PBKDF_CURRENT, currentPw, PBKDF2_HASH_ROUNDS)
&& !(pbkdf2Fallback && passwordMatchesSaved(PBKDF_FALLBACK,
currentPw, PBKDF2_HASH_ROUNDS))) {
if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting");
return false;
}
}
return true;
| boolean | beginFullBackup(FullBackupJob scheduledJob)Conditions are right for a full backup operation, so run one. The model we use is
to perform one app backup per scheduled job execution, and to reschedule the job
with zero latency as long as conditions remain right and we still have work to do.
long now = System.currentTimeMillis();
FullBackupEntry entry = null;
if (!mEnabled || !mProvisioned) {
// Backups are globally disabled, so don't proceed. We also don't reschedule
// the job driving automatic backups; that job will be scheduled again when
// the user enables backup.
if (MORE_DEBUG) {
Slog.i(TAG, "beginFullBackup but e=" + mEnabled
+ " p=" + mProvisioned + "; ignoring");
}
return false;
}
if (DEBUG_SCHEDULING) {
Slog.i(TAG, "Beginning scheduled full backup operation");
}
// Great; we're able to run full backup jobs now. See if we have any work to do.
synchronized (mQueueLock) {
if (mRunningFullBackupTask != null) {
Slog.e(TAG, "Backup triggered but one already/still running!");
return false;
}
if (mFullBackupQueue.size() == 0) {
// no work to do so just bow out
if (DEBUG) {
Slog.i(TAG, "Backup queue empty; doing nothing");
}
return false;
}
entry = mFullBackupQueue.get(0);
long timeSinceRun = now - entry.lastBackup;
if (timeSinceRun < MIN_FULL_BACKUP_INTERVAL) {
// It's too early to back up the next thing in the queue, so bow out
if (MORE_DEBUG) {
Slog.i(TAG, "Device ready but too early to back up next app");
}
final long latency = MIN_FULL_BACKUP_INTERVAL - timeSinceRun;
mBackupHandler.post(new Runnable() {
@Override public void run() {
FullBackupJob.schedule(mContext, latency);
}
});
return false;
}
// Okay, the top thing is runnable now. Pop it off and get going.
mFullBackupQueue.remove(0);
AtomicBoolean latch = new AtomicBoolean(false);
String[] pkg = new String[] {entry.packageName};
mRunningFullBackupTask = new PerformFullTransportBackupTask(null, pkg, true,
scheduledJob, latch);
(new Thread(mRunningFullBackupTask)).start();
}
return true;
| public android.app.backup.IRestoreSession | beginRestoreSession(java.lang.String packageName, java.lang.String transport)
if (DEBUG) Slog.v(TAG, "beginRestoreSession: pkg=" + packageName
+ " transport=" + transport);
boolean needPermission = true;
if (transport == null) {
transport = mCurrentTransport;
if (packageName != null) {
PackageInfo app = null;
try {
app = mPackageManager.getPackageInfo(packageName, 0);
} catch (NameNotFoundException nnf) {
Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
throw new IllegalArgumentException("Package " + packageName + " not found");
}
if (app.applicationInfo.uid == Binder.getCallingUid()) {
// So: using the current active transport, and the caller has asked
// that its own package will be restored. In this narrow use case
// we do not require the caller to hold the permission.
needPermission = false;
}
}
}
if (needPermission) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"beginRestoreSession");
} else {
if (DEBUG) Slog.d(TAG, "restoring self on current transport; no permission needed");
}
synchronized(this) {
if (mActiveRestoreSession != null) {
Slog.d(TAG, "Restore session requested but one already active");
return null;
}
mActiveRestoreSession = new ActiveRestoreSession(packageName, transport);
mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT, TIMEOUT_RESTORE_INTERVAL);
}
return mActiveRestoreSession;
| android.app.IBackupAgent | bindToAgentSynchronous(android.content.pm.ApplicationInfo app, int mode)
IBackupAgent agent = null;
synchronized(mAgentConnectLock) {
mConnecting = true;
mConnectedAgent = null;
try {
if (mActivityManager.bindBackupAgent(app, mode)) {
Slog.d(TAG, "awaiting agent for " + app);
// success; wait for the agent to arrive
// only wait 10 seconds for the bind to happen
long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
while (mConnecting && mConnectedAgent == null
&& (System.currentTimeMillis() < timeoutMark)) {
try {
mAgentConnectLock.wait(5000);
} catch (InterruptedException e) {
// just bail
if (DEBUG) Slog.w(TAG, "Interrupted: " + e);
mActivityManager.clearPendingBackup();
return null;
}
}
// if we timed out with no connect, abort and move on
if (mConnecting == true) {
Slog.w(TAG, "Timeout waiting for agent " + app);
mActivityManager.clearPendingBackup();
return null;
}
if (DEBUG) Slog.i(TAG, "got agent " + mConnectedAgent);
agent = mConnectedAgent;
}
} catch (RemoteException e) {
// can't happen - ActivityManager is local
}
}
return agent;
| boolean | bindTransport(android.content.pm.ServiceInfo transport)
ComponentName svcName = new ComponentName(transport.packageName, transport.name);
if (DEBUG) {
Slog.i(TAG, "Binding to transport host " + svcName);
}
Intent intent = new Intent(mTransportServiceIntent);
intent.setComponent(svcName);
TransportConnection connection;
synchronized (mTransports) {
connection = mTransportConnections.get(transport.packageName);
if (null == connection) {
connection = new TransportConnection(transport);
mTransportConnections.put(transport.packageName, connection);
} else {
// This is a rebind due to package upgrade. The service won't be
// automatically relaunched for us until we explicitly rebind, but
// we need to unbind the now-orphaned original connection.
mContext.unbindService(connection);
}
}
return mContext.bindServiceAsUser(intent,
connection, Context.BIND_AUTO_CREATE,
UserHandle.OWNER);
| private javax.crypto.SecretKey | buildCharArrayKey(java.lang.String algorithm, char[] pwArray, byte[] salt, int rounds)
try {
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(algorithm);
KeySpec ks = new PBEKeySpec(pwArray, salt, rounds, PBKDF2_KEY_SIZE);
return keyFactory.generateSecret(ks);
} catch (InvalidKeySpecException e) {
Slog.e(TAG, "Invalid key spec for PBKDF2!");
} catch (NoSuchAlgorithmException e) {
Slog.e(TAG, "PBKDF2 unavailable!");
}
return null;
| private java.lang.String | buildPasswordHash(java.lang.String algorithm, java.lang.String pw, byte[] salt, int rounds)
SecretKey key = buildPasswordKey(algorithm, pw, salt, rounds);
if (key != null) {
return byteArrayToHex(key.getEncoded());
}
return null;
| private javax.crypto.SecretKey | buildPasswordKey(java.lang.String algorithm, java.lang.String pw, byte[] salt, int rounds)
return buildCharArrayKey(algorithm, pw.toCharArray(), salt, rounds);
| private java.lang.String | byteArrayToHex(byte[] data)
StringBuilder buf = new StringBuilder(data.length * 2);
for (int i = 0; i < data.length; i++) {
buf.append(Byte.toHexString(data[i], true));
}
return buf.toString();
| void | checkForTransportAndBind(android.content.pm.PackageInfo pkgInfo)
Intent intent = new Intent(mTransportServiceIntent)
.setPackage(pkgInfo.packageName);
List<ResolveInfo> hosts = mPackageManager.queryIntentServicesAsUser(
intent, 0, UserHandle.USER_OWNER);
final int N = hosts.size();
for (int i = 0; i < N; i++) {
final ServiceInfo info = hosts.get(i).serviceInfo;
tryBindTransport(info);
}
| void | clearApplicationDataSynchronous(java.lang.String packageName)
// Don't wipe packages marked allowClearUserData=false
try {
PackageInfo info = mPackageManager.getPackageInfo(packageName, 0);
if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) == 0) {
if (MORE_DEBUG) Slog.i(TAG, "allowClearUserData=false so not wiping "
+ packageName);
return;
}
} catch (NameNotFoundException e) {
Slog.w(TAG, "Tried to clear data for " + packageName + " but not found");
return;
}
ClearDataObserver observer = new ClearDataObserver();
synchronized(mClearDataLock) {
mClearingData = true;
try {
mActivityManager.clearApplicationUserData(packageName, observer, 0);
} catch (RemoteException e) {
// can't happen because the activity manager is in this process
}
// only wait 10 seconds for the clear data to happen
long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
while (mClearingData && (System.currentTimeMillis() < timeoutMark)) {
try {
mClearDataLock.wait(5000);
} catch (InterruptedException e) {
// won't happen, but still.
mClearingData = false;
}
}
}
| public void | clearBackupData(java.lang.String transportName, java.lang.String packageName)
if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName + " on " + transportName);
PackageInfo info;
try {
info = mPackageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
} catch (NameNotFoundException e) {
Slog.d(TAG, "No such package '" + packageName + "' - not clearing backup data");
return;
}
// If the caller does not hold the BACKUP permission, it can only request a
// wipe of its own backed-up data.
HashSet<String> apps;
if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
apps = mBackupParticipants.get(Binder.getCallingUid());
} else {
// a caller with full permission can ask to back up any participating app
// !!! TODO: allow data-clear of ANY app?
if (DEBUG) Slog.v(TAG, "Privileged caller, allowing clear of other apps");
apps = new HashSet<String>();
int N = mBackupParticipants.size();
for (int i = 0; i < N; i++) {
HashSet<String> s = mBackupParticipants.valueAt(i);
if (s != null) {
apps.addAll(s);
}
}
}
// Is the given app an available participant?
if (apps.contains(packageName)) {
// found it; fire off the clear request
if (DEBUG) Slog.v(TAG, "Found the app - running clear process");
mBackupHandler.removeMessages(MSG_RETRY_CLEAR);
synchronized (mQueueLock) {
final IBackupTransport transport = getTransport(transportName);
if (transport == null) {
// transport is currently unavailable -- make sure to retry
Message msg = mBackupHandler.obtainMessage(MSG_RETRY_CLEAR,
new ClearRetryParams(transportName, packageName));
mBackupHandler.sendMessageDelayed(msg, TRANSPORT_RETRY_INTERVAL);
return;
}
long oldId = Binder.clearCallingIdentity();
mWakelock.acquire();
Message msg = mBackupHandler.obtainMessage(MSG_RUN_CLEAR,
new ClearParams(transport, info));
mBackupHandler.sendMessage(msg);
Binder.restoreCallingIdentity(oldId);
}
}
| void | clearBackupTrace()
if (DEBUG_BACKUP_TRACE) {
synchronized (mBackupTrace) {
mBackupTrace.clear();
}
}
| void | clearRestoreSession(com.android.server.backup.BackupManagerService$ActiveRestoreSession currentSession)
synchronized(this) {
if (currentSession != mActiveRestoreSession) {
Slog.e(TAG, "ending non-current restore session");
} else {
if (DEBUG) Slog.v(TAG, "Clearing restore session and halting timeout");
mActiveRestoreSession = null;
mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
}
}
| public void | dataChanged(java.lang.String packageName)
final int callingUserHandle = UserHandle.getCallingUserId();
if (callingUserHandle != UserHandle.USER_OWNER) {
// App is running under a non-owner user profile. For now, we do not back
// up data from secondary user profiles.
// TODO: backups for all user profiles.
if (MORE_DEBUG) {
Slog.v(TAG, "dataChanged(" + packageName + ") ignored because it's user "
+ callingUserHandle);
}
return;
}
final HashSet<String> targets = dataChangedTargets(packageName);
if (targets == null) {
Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
+ " uid=" + Binder.getCallingUid());
return;
}
mBackupHandler.post(new Runnable() {
public void run() {
dataChangedImpl(packageName, targets);
}
});
| private void | dataChangedImpl(java.lang.String packageName)
HashSet<String> targets = dataChangedTargets(packageName);
dataChangedImpl(packageName, targets);
| private void | dataChangedImpl(java.lang.String packageName, java.util.HashSet targets)
// Record that we need a backup pass for the caller. Since multiple callers
// may share a uid, we need to note all candidates within that uid and schedule
// a backup pass for each of them.
if (targets == null) {
Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
+ " uid=" + Binder.getCallingUid());
return;
}
synchronized (mQueueLock) {
// Note that this client has made data changes that need to be backed up
if (targets.contains(packageName)) {
// Add the caller to the set of pending backups. If there is
// one already there, then overwrite it, but no harm done.
BackupRequest req = new BackupRequest(packageName);
if (mPendingBackups.put(packageName, req) == null) {
if (DEBUG) Slog.d(TAG, "Now staging backup of " + packageName);
// Journal this request in case of crash. The put()
// operation returned null when this package was not already
// in the set; we want to avoid touching the disk redundantly.
writeToJournalLocked(packageName);
if (MORE_DEBUG) {
int numKeys = mPendingBackups.size();
Slog.d(TAG, "Now awaiting backup for " + numKeys + " participants:");
for (BackupRequest b : mPendingBackups.values()) {
Slog.d(TAG, " + " + b);
}
}
}
}
}
| private java.util.HashSet | dataChangedTargets(java.lang.String packageName)
// If the caller does not hold the BACKUP permission, it can only request a
// backup of its own data.
if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
synchronized (mBackupParticipants) {
return mBackupParticipants.get(Binder.getCallingUid());
}
}
// a caller with full permission can ask to back up any participating app
// !!! TODO: allow backup of ANY app?
HashSet<String> targets = new HashSet<String>();
synchronized (mBackupParticipants) {
int N = mBackupParticipants.size();
for (int i = 0; i < N; i++) {
HashSet<String> s = mBackupParticipants.valueAt(i);
if (s != null) {
targets.addAll(s);
}
}
}
return targets;
| boolean | deviceIsEncrypted()
try {
return mMountService.getEncryptionState()
!= IMountService.ENCRYPTION_STATE_NONE
&& mMountService.getPasswordType()
!= StorageManager.CRYPT_TYPE_DEFAULT;
} catch (Exception e) {
// If we can't talk to the mount service we have a serious problem; fail
// "secure" i.e. assuming that the device is encrypted.
Slog.e(TAG, "Unable to communicate with mount service: " + e.getMessage());
return true;
}
| boolean | deviceIsProvisioned()
final ContentResolver resolver = mContext.getContentResolver();
return (Settings.Global.getInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0) != 0);
| public void | dump(java.io.FileDescriptor fd, java.io.PrintWriter pw, java.lang.String[] args)
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
long identityToken = Binder.clearCallingIdentity();
try {
if (args != null) {
for (String arg : args) {
if ("-h".equals(arg)) {
pw.println("'dumpsys backup' optional arguments:");
pw.println(" -h : this help text");
pw.println(" a[gents] : dump information about defined backup agents");
return;
} else if ("agents".startsWith(arg)) {
dumpAgents(pw);
return;
}
}
}
dumpInternal(pw);
} finally {
Binder.restoreCallingIdentity(identityToken);
}
| private void | dumpAgents(java.io.PrintWriter pw)
List<PackageInfo> agentPackages = allAgentPackages();
pw.println("Defined backup agents:");
for (PackageInfo pkg : agentPackages) {
pw.print(" ");
pw.print(pkg.packageName); pw.println(':");
pw.print(" "); pw.println(pkg.applicationInfo.backupAgentName);
}
| private void | dumpInternal(java.io.PrintWriter pw)
synchronized (mQueueLock) {
pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled")
+ " / " + (!mProvisioned ? "not " : "") + "provisioned / "
+ (this.mPendingInits.size() == 0 ? "not " : "") + "pending init");
pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled"));
if (mBackupRunning) pw.println("Backup currently running");
pw.println("Last backup pass started: " + mLastBackupPass
+ " (now = " + System.currentTimeMillis() + ')");
pw.println(" next scheduled: " + mNextBackupPass);
pw.println("Available transports:");
final String[] transports = listAllTransports();
if (transports != null) {
for (String t : listAllTransports()) {
pw.println((t.equals(mCurrentTransport) ? " * " : " ") + t);
try {
IBackupTransport transport = getTransport(t);
File dir = new File(mBaseStateDir, transport.transportDirName());
pw.println(" destination: " + transport.currentDestinationString());
pw.println(" intent: " + transport.configurationIntent());
for (File f : dir.listFiles()) {
pw.println(" " + f.getName() + " - " + f.length() + " state bytes");
}
} catch (Exception e) {
Slog.e(TAG, "Error in transport", e);
pw.println(" Error: " + e);
}
}
}
pw.println("Pending init: " + mPendingInits.size());
for (String s : mPendingInits) {
pw.println(" " + s);
}
if (DEBUG_BACKUP_TRACE) {
synchronized (mBackupTrace) {
if (!mBackupTrace.isEmpty()) {
pw.println("Most recent backup trace:");
for (String s : mBackupTrace) {
pw.println(" " + s);
}
}
}
}
int N = mBackupParticipants.size();
pw.println("Participants:");
for (int i=0; i<N; i++) {
int uid = mBackupParticipants.keyAt(i);
pw.print(" uid: ");
pw.println(uid);
HashSet<String> participants = mBackupParticipants.valueAt(i);
for (String app: participants) {
pw.println(" " + app);
}
}
pw.println("Ancestral packages: "
+ (mAncestralPackages == null ? "none" : mAncestralPackages.size()));
if (mAncestralPackages != null) {
for (String pkg : mAncestralPackages) {
pw.println(" " + pkg);
}
}
pw.println("Ever backed up: " + mEverStoredApps.size());
for (String pkg : mEverStoredApps) {
pw.println(" " + pkg);
}
pw.println("Pending key/value backup: " + mPendingBackups.size());
for (BackupRequest req : mPendingBackups.values()) {
pw.println(" " + req);
}
pw.println("Full backup queue:" + mFullBackupQueue.size());
for (FullBackupEntry entry : mFullBackupQueue) {
pw.print(" "); pw.print(entry.lastBackup);
pw.print(" : "); pw.println(entry.packageName);
}
}
| void | endFullBackup()
synchronized (mQueueLock) {
if (mRunningFullBackupTask != null) {
if (DEBUG_SCHEDULING) {
Slog.i(TAG, "Telling running backup to stop");
}
mRunningFullBackupTask.setRunning(false);
}
}
| void | enqueueFullBackup(java.lang.String packageName, long lastBackedUp)Enqueue full backup for the given app, with a note about when it last ran.
FullBackupEntry newEntry = new FullBackupEntry(packageName, lastBackedUp);
synchronized (mQueueLock) {
int N = mFullBackupQueue.size();
// First, sanity check that we aren't adding a duplicate. Slow but
// straightforward; we'll have at most on the order of a few hundred
// items in this list.
for (int i = N-1; i >= 0; i--) {
final FullBackupEntry e = mFullBackupQueue.get(i);
if (packageName.equals(e.packageName)) {
if (DEBUG) {
Slog.w(TAG, "Removing schedule queue dupe of " + packageName);
}
mFullBackupQueue.remove(i);
}
}
// This is also slow but easy for modest numbers of apps: work backwards
// from the end of the queue until we find an item whose last backup
// time was before this one, then insert this new entry after it.
int which;
for (which = mFullBackupQueue.size() - 1; which >= 0; which--) {
final FullBackupEntry entry = mFullBackupQueue.get(which);
if (entry.lastBackup <= lastBackedUp) {
mFullBackupQueue.add(which + 1, newEntry);
break;
}
}
if (which < 0) {
// this one is earlier than any existing one, so prepend
mFullBackupQueue.add(0, newEntry);
}
}
writeFullBackupScheduleAsync();
| public void | fullBackup(android.os.ParcelFileDescriptor fd, boolean includeApks, boolean includeObbs, boolean includeShared, boolean doWidgets, boolean doAllApps, boolean includeSystem, boolean compress, java.lang.String[] pkgList)
mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
final int callingUserHandle = UserHandle.getCallingUserId();
if (callingUserHandle != UserHandle.USER_OWNER) {
throw new IllegalStateException("Backup supported only for the device owner");
}
// Validate
if (!doAllApps) {
if (!includeShared) {
// If we're backing up shared data (sdcard or equivalent), then we can run
// without any supplied app names. Otherwise, we'd be doing no work, so
// report the error.
if (pkgList == null || pkgList.length == 0) {
throw new IllegalArgumentException(
"Backup requested but neither shared nor any apps named");
}
}
}
long oldId = Binder.clearCallingIdentity();
try {
// Doesn't make sense to do a full backup prior to setup
if (!deviceIsProvisioned()) {
Slog.i(TAG, "Full backup not supported before setup");
return;
}
if (DEBUG) Slog.v(TAG, "Requesting full backup: apks=" + includeApks
+ " obb=" + includeObbs + " shared=" + includeShared + " all=" + doAllApps
+ " system=" + includeSystem + " pkgs=" + pkgList);
Slog.i(TAG, "Beginning full backup...");
FullBackupParams params = new FullBackupParams(fd, includeApks, includeObbs,
includeShared, doWidgets, doAllApps, includeSystem, compress, pkgList);
final int token = generateToken();
synchronized (mFullConfirmations) {
mFullConfirmations.put(token, params);
}
// start up the confirmation UI
if (DEBUG) Slog.d(TAG, "Starting backup confirmation UI, token=" + token);
if (!startConfirmationUi(token, FullBackup.FULL_BACKUP_INTENT_ACTION)) {
Slog.e(TAG, "Unable to launch full backup confirmation");
mFullConfirmations.delete(token);
return;
}
// make sure the screen is lit for the user interaction
mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
// start the confirmation countdown
startConfirmationTimeout(token, params);
// wait for the backup to be performed
if (DEBUG) Slog.d(TAG, "Waiting for full backup completion...");
waitForCompletion(params);
} finally {
try {
fd.close();
} catch (IOException e) {
// just eat it
}
Binder.restoreCallingIdentity(oldId);
Slog.d(TAG, "Full backup processing complete.");
}
| public void | fullRestore(android.os.ParcelFileDescriptor fd)
mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullRestore");
final int callingUserHandle = UserHandle.getCallingUserId();
if (callingUserHandle != UserHandle.USER_OWNER) {
throw new IllegalStateException("Restore supported only for the device owner");
}
long oldId = Binder.clearCallingIdentity();
try {
// Check whether the device has been provisioned -- we don't handle
// full restores prior to completing the setup process.
if (!deviceIsProvisioned()) {
Slog.i(TAG, "Full restore not permitted before setup");
return;
}
Slog.i(TAG, "Beginning full restore...");
FullRestoreParams params = new FullRestoreParams(fd);
final int token = generateToken();
synchronized (mFullConfirmations) {
mFullConfirmations.put(token, params);
}
// start up the confirmation UI
if (DEBUG) Slog.d(TAG, "Starting restore confirmation UI, token=" + token);
if (!startConfirmationUi(token, FullBackup.FULL_RESTORE_INTENT_ACTION)) {
Slog.e(TAG, "Unable to launch full restore confirmation");
mFullConfirmations.delete(token);
return;
}
// make sure the screen is lit for the user interaction
mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
// start the confirmation countdown
startConfirmationTimeout(token, params);
// wait for the restore to be performed
if (DEBUG) Slog.d(TAG, "Waiting for full restore completion...");
waitForCompletion(params);
} finally {
try {
fd.close();
} catch (IOException e) {
Slog.w(TAG, "Error trying to close fd after full restore: " + e);
}
Binder.restoreCallingIdentity(oldId);
Slog.i(TAG, "Full restore processing complete.");
}
| public void | fullTransportBackup(java.lang.String[] pkgNames)
mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
"fullTransportBackup");
final int callingUserHandle = UserHandle.getCallingUserId();
if (callingUserHandle != UserHandle.USER_OWNER) {
throw new IllegalStateException("Restore supported only for the device owner");
}
if (DEBUG) {
Slog.d(TAG, "fullTransportBackup()");
}
AtomicBoolean latch = new AtomicBoolean(false);
PerformFullTransportBackupTask task =
new PerformFullTransportBackupTask(null, pkgNames, false, null, latch);
(new Thread(task, "full-transport-master")).start();
synchronized (latch) {
try {
while (latch.get() == false) {
latch.wait();
}
} catch (InterruptedException e) {}
}
if (DEBUG) {
Slog.d(TAG, "Done with full transport backup.");
}
| int | generateToken()
int token;
do {
synchronized (mTokenGenerator) {
token = mTokenGenerator.nextInt();
}
} while (token < 0);
return token;
| long | getAvailableRestoreToken(java.lang.String packageName)
long token = mAncestralToken;
synchronized (mQueueLock) {
if (mEverStoredApps.contains(packageName)) {
token = mCurrentToken;
}
}
return token;
| public android.content.Intent | getConfigurationIntent(java.lang.String transportName)
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"getConfigurationIntent");
synchronized (mTransports) {
final IBackupTransport transport = mTransports.get(transportName);
if (transport != null) {
try {
final Intent intent = transport.configurationIntent();
if (MORE_DEBUG) Slog.d(TAG, "getConfigurationIntent() returning config intent "
+ intent);
return intent;
} catch (RemoteException e) {
/* fall through to return null */
}
}
}
return null;
| public java.lang.String | getCurrentTransport()
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"getCurrentTransport");
if (MORE_DEBUG) Slog.v(TAG, "... getCurrentTransport() returning " + mCurrentTransport);
return mCurrentTransport;
| public android.content.Intent | getDataManagementIntent(java.lang.String transportName)
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"getDataManagementIntent");
synchronized (mTransports) {
final IBackupTransport transport = mTransports.get(transportName);
if (transport != null) {
try {
final Intent intent = transport.dataManagementIntent();
if (MORE_DEBUG) Slog.d(TAG, "getDataManagementIntent() returning intent "
+ intent);
return intent;
} catch (RemoteException e) {
/* fall through to return null */
}
}
}
return null;
| public java.lang.String | getDataManagementLabel(java.lang.String transportName)
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"getDataManagementLabel");
synchronized (mTransports) {
final IBackupTransport transport = mTransports.get(transportName);
if (transport != null) {
try {
final String text = transport.dataManagementLabel();
if (MORE_DEBUG) Slog.d(TAG, "getDataManagementLabel() returning " + text);
return text;
} catch (RemoteException e) {
/* fall through to return null */
}
}
}
return null;
| public java.lang.String | getDestinationString(java.lang.String transportName)
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"getDestinationString");
synchronized (mTransports) {
final IBackupTransport transport = mTransports.get(transportName);
if (transport != null) {
try {
final String text = transport.currentDestinationString();
if (MORE_DEBUG) Slog.d(TAG, "getDestinationString() returning " + text);
return text;
} catch (RemoteException e) {
/* fall through to return null */
}
}
}
return null;
| static Trampoline | getInstance()
// Always constructed during system bringup, so no need to lazy-init
return sInstance;
| private com.android.internal.backup.IBackupTransport | getTransport(java.lang.String transportName)
synchronized (mTransports) {
IBackupTransport transport = mTransports.get(transportName);
if (transport == null) {
Slog.w(TAG, "Requested unavailable transport: " + transportName);
}
return transport;
}
| void | handleTimeout(int token, java.lang.Object obj)
// Notify any synchronous waiters
Operation op = null;
synchronized (mCurrentOpLock) {
op = mCurrentOperations.get(token);
if (MORE_DEBUG) {
if (op == null) Slog.w(TAG, "Timeout of token " + Integer.toHexString(token)
+ " but no op found");
}
int state = (op != null) ? op.state : OP_TIMEOUT;
if (state == OP_PENDING) {
if (DEBUG) Slog.v(TAG, "TIMEOUT: token=" + Integer.toHexString(token));
op.state = OP_TIMEOUT;
mCurrentOperations.put(token, op);
}
mCurrentOpLock.notifyAll();
}
// If there's a TimeoutHandler for this event, call it
if (op != null && op.callback != null) {
op.callback.handleTimeout();
}
| public boolean | hasBackupPassword()
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"hasBackupPassword");
return mPasswordHash != null && mPasswordHash.length() > 0;
| static byte[] | hashSignature(android.content.pm.Signature sig)
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.update(sig.toByteArray());
return digest.digest();
} catch (NoSuchAlgorithmException e) {
Slog.w(TAG, "No SHA-256 algorithm found!");
}
return null;
| private byte[] | hexToByteArray(java.lang.String digits)
final int bytes = digits.length() / 2;
if (2*bytes != digits.length()) {
throw new IllegalArgumentException("Hex string must have an even number of digits");
}
byte[] result = new byte[bytes];
for (int i = 0; i < digits.length(); i += 2) {
result[i/2] = (byte) Integer.parseInt(digits.substring(i, i+2), 16);
}
return result;
| private void | initPackageTracking()
if (MORE_DEBUG) Slog.v(TAG, "` tracking");
// Remember our ancestral dataset
mTokenFile = new File(mBaseStateDir, "ancestral");
try {
RandomAccessFile tf = new RandomAccessFile(mTokenFile, "r");
int version = tf.readInt();
if (version == CURRENT_ANCESTRAL_RECORD_VERSION) {
mAncestralToken = tf.readLong();
mCurrentToken = tf.readLong();
int numPackages = tf.readInt();
if (numPackages >= 0) {
mAncestralPackages = new HashSet<String>();
for (int i = 0; i < numPackages; i++) {
String pkgName = tf.readUTF();
mAncestralPackages.add(pkgName);
}
}
}
tf.close();
} catch (FileNotFoundException fnf) {
// Probably innocuous
Slog.v(TAG, "No ancestral data");
} catch (IOException e) {
Slog.w(TAG, "Unable to read token file", e);
}
// Keep a log of what apps we've ever backed up. Because we might have
// rebooted in the middle of an operation that was removing something from
// this log, we sanity-check its contents here and reconstruct it.
mEverStored = new File(mBaseStateDir, "processed");
File tempProcessedFile = new File(mBaseStateDir, "processed.new");
// If we were in the middle of removing something from the ever-backed-up
// file, there might be a transient "processed.new" file still present.
// Ignore it -- we'll validate "processed" against the current package set.
if (tempProcessedFile.exists()) {
tempProcessedFile.delete();
}
// If there are previous contents, parse them out then start a new
// file to continue the recordkeeping.
if (mEverStored.exists()) {
RandomAccessFile temp = null;
RandomAccessFile in = null;
try {
temp = new RandomAccessFile(tempProcessedFile, "rws");
in = new RandomAccessFile(mEverStored, "r");
while (true) {
PackageInfo info;
String pkg = in.readUTF();
try {
info = mPackageManager.getPackageInfo(pkg, 0);
mEverStoredApps.add(pkg);
temp.writeUTF(pkg);
if (MORE_DEBUG) Slog.v(TAG, " + " + pkg);
} catch (NameNotFoundException e) {
// nope, this package was uninstalled; don't include it
if (MORE_DEBUG) Slog.v(TAG, " - " + pkg);
}
}
} catch (EOFException e) {
// Once we've rewritten the backup history log, atomically replace the
// old one with the new one then reopen the file for continuing use.
if (!tempProcessedFile.renameTo(mEverStored)) {
Slog.e(TAG, "Error renaming " + tempProcessedFile + " to " + mEverStored);
}
} catch (IOException e) {
Slog.e(TAG, "Error in processed file", e);
} finally {
try { if (temp != null) temp.close(); } catch (IOException e) {}
try { if (in != null) in.close(); } catch (IOException e) {}
}
}
// Resume the full-data backup queue
mFullBackupQueue = readFullBackupSchedule();
// Register for broadcasts about package install, etc., so we can
// update the provider list.
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_PACKAGE_ADDED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
filter.addDataScheme("package");
mContext.registerReceiver(mBroadcastReceiver, filter);
// Register for events related to sdcard installation.
IntentFilter sdFilter = new IntentFilter();
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
mContext.registerReceiver(mBroadcastReceiver, sdFilter);
| public boolean | isBackupEnabled()
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "isBackupEnabled");
return mEnabled; // no need to synchronize just to read it
| public java.lang.String[] | listAllTransports()
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "listAllTransports");
String[] list = null;
ArrayList<String> known = new ArrayList<String>();
for (Map.Entry<String, IBackupTransport> entry : mTransports.entrySet()) {
if (entry.getValue() != null) {
known.add(entry.getKey());
}
}
if (known.size() > 0) {
list = new String[known.size()];
known.toArray(list);
}
return list;
| void | logBackupComplete(java.lang.String packageName)
if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) return;
synchronized (mEverStoredApps) {
if (!mEverStoredApps.add(packageName)) return;
RandomAccessFile out = null;
try {
out = new RandomAccessFile(mEverStored, "rws");
out.seek(out.length());
out.writeUTF(packageName);
} catch (IOException e) {
Slog.e(TAG, "Can't log backup of " + packageName + " to " + mEverStored);
} finally {
try { if (out != null) out.close(); } catch (IOException e) {}
}
}
| private byte[] | makeKeyChecksum(java.lang.String algorithm, byte[] pwBytes, byte[] salt, int rounds)
char[] mkAsChar = new char[pwBytes.length];
for (int i = 0; i < pwBytes.length; i++) {
mkAsChar[i] = (char) pwBytes[i];
}
Key checksum = buildCharArrayKey(algorithm, mkAsChar, salt, rounds);
return checksum.getEncoded();
| public void | opComplete(int token)
if (MORE_DEBUG) Slog.v(TAG, "opComplete: " + Integer.toHexString(token));
Operation op = null;
synchronized (mCurrentOpLock) {
op = mCurrentOperations.get(token);
if (op != null) {
op.state = OP_ACKNOWLEDGED;
}
mCurrentOpLock.notifyAll();
}
// The completion callback, if any, is invoked on the handler
if (op != null && op.callback != null) {
Message msg = mBackupHandler.obtainMessage(MSG_OP_COMPLETE, op.callback);
mBackupHandler.sendMessage(msg);
}
| private void | parseLeftoverJournals()
for (File f : mJournalDir.listFiles()) {
if (mJournal == null || 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.
RandomAccessFile in = null;
try {
Slog.i(TAG, "Found stale backup journal, scheduling");
in = new RandomAccessFile(f, "r");
while (true) {
String packageName = in.readUTF();
if (MORE_DEBUG) Slog.i(TAG, " " + packageName);
dataChangedImpl(packageName);
}
} catch (EOFException e) {
// no more data; we're done
} catch (Exception e) {
Slog.e(TAG, "Can't read " + f, e);
} finally {
// close/delete the file
try { if (in != null) in.close(); } catch (IOException e) {}
f.delete();
}
}
}
| boolean | passwordMatchesSaved(java.lang.String algorithm, java.lang.String candidatePw, int rounds)
if (mPasswordHash == null) {
// no current password case -- require that 'currentPw' be null or empty
if (candidatePw == null || "".equals(candidatePw)) {
return true;
} // else the non-empty candidate does not match the empty stored pw
} else {
// hash the stated current pw and compare to the stored one
if (candidatePw != null && candidatePw.length() > 0) {
String currentPwHash = buildPasswordHash(algorithm, candidatePw, mPasswordSalt, rounds);
if (mPasswordHash.equalsIgnoreCase(currentPwHash)) {
// candidate hash matches the stored hash -- the password matches
return true;
}
} // else the stored pw is nonempty but the candidate is empty; no match
}
return false;
| void | prepareOperationTimeout(int token, long interval, com.android.server.backup.BackupManagerService$BackupRestoreTask callback)
if (MORE_DEBUG) Slog.v(TAG, "starting timeout: token=" + Integer.toHexString(token)
+ " interval=" + interval);
synchronized (mCurrentOpLock) {
mCurrentOperations.put(token, new Operation(OP_PENDING, callback));
Message msg = mBackupHandler.obtainMessage(MSG_TIMEOUT, token, 0, callback);
mBackupHandler.sendMessageDelayed(msg, interval);
}
| private byte[] | randomBytes(int bits)
byte[] array = new byte[bits / 8];
mRng.nextBytes(array);
return array;
| private java.util.ArrayList | readFullBackupSchedule()
ArrayList<FullBackupEntry> schedule = null;
synchronized (mQueueLock) {
if (mFullBackupScheduleFile.exists()) {
FileInputStream fstream = null;
BufferedInputStream bufStream = null;
DataInputStream in = null;
try {
fstream = new FileInputStream(mFullBackupScheduleFile);
bufStream = new BufferedInputStream(fstream);
in = new DataInputStream(bufStream);
int version = in.readInt();
if (version != SCHEDULE_FILE_VERSION) {
Slog.e(TAG, "Unknown backup schedule version " + version);
return null;
}
int N = in.readInt();
schedule = new ArrayList<FullBackupEntry>(N);
for (int i = 0; i < N; i++) {
String pkgName = in.readUTF();
long lastBackup = in.readLong();
schedule.add(new FullBackupEntry(pkgName, lastBackup));
}
Collections.sort(schedule);
} catch (Exception e) {
Slog.e(TAG, "Unable to read backup schedule", e);
mFullBackupScheduleFile.delete();
schedule = null;
} finally {
IoUtils.closeQuietly(in);
IoUtils.closeQuietly(bufStream);
IoUtils.closeQuietly(fstream);
}
}
if (schedule == null) {
// no prior queue record, or unable to read it. Set up the queue
// from scratch.
List<PackageInfo> apps =
PackageManagerBackupAgent.getStorableApplications(mPackageManager);
final int N = apps.size();
schedule = new ArrayList<FullBackupEntry>(N);
for (int i = 0; i < N; i++) {
PackageInfo info = apps.get(i);
if (appGetsFullBackup(info)) {
schedule.add(new FullBackupEntry(info.packageName, 0));
}
}
writeFullBackupScheduleAsync();
}
}
return schedule;
| void | recordInitPendingLocked(boolean isPending, java.lang.String transportName)
if (DEBUG) Slog.i(TAG, "recordInitPendingLocked: " + isPending
+ " on transport " + transportName);
mBackupHandler.removeMessages(MSG_RETRY_INIT);
try {
IBackupTransport transport = getTransport(transportName);
if (transport != null) {
String transportDirName = transport.transportDirName();
File stateDir = new File(mBaseStateDir, transportDirName);
File initPendingFile = new File(stateDir, INIT_SENTINEL_FILE_NAME);
if (isPending) {
// We need an init before we can proceed with sending backup data.
// Record that with an entry in our set of pending inits, as well as
// journaling it via creation of a sentinel file.
mPendingInits.add(transportName);
try {
(new FileOutputStream(initPendingFile)).close();
} catch (IOException ioe) {
// Something is badly wrong with our permissions; just try to move on
}
} else {
// No more initialization needed; wipe the journal and reset our state.
initPendingFile.delete();
mPendingInits.remove(transportName);
}
return; // done; don't fall through to the error case
}
} catch (RemoteException e) {
// transport threw when asked its name; fall through to the lookup-failed case
}
// The named transport doesn't exist or threw. This operation is
// important, so we record the need for a an init and post a message
// to retry the init later.
if (isPending) {
mPendingInits.add(transportName);
mBackupHandler.sendMessageDelayed(
mBackupHandler.obtainMessage(MSG_RETRY_INIT,
(isPending ? 1 : 0),
0,
transportName),
TRANSPORT_RETRY_INTERVAL);
}
| private void | registerTransport(java.lang.String name, java.lang.String component, com.android.internal.backup.IBackupTransport transport)
synchronized (mTransports) {
if (DEBUG) Slog.v(TAG, "Registering transport "
+ component + "::" + name + " = " + transport);
if (transport != null) {
mTransports.put(name, transport);
mTransportNames.put(component, name);
} else {
mTransports.remove(mTransportNames.get(component));
mTransportNames.remove(component);
// Nothing further to do in the unregistration case
return;
}
}
// If the init sentinel file exists, we need to be sure to perform the init
// as soon as practical. We also create the state directory at registration
// time to ensure it's present from the outset.
try {
String transportName = transport.transportDirName();
File stateDir = new File(mBaseStateDir, transportName);
stateDir.mkdirs();
File initSentinel = new File(stateDir, INIT_SENTINEL_FILE_NAME);
if (initSentinel.exists()) {
synchronized (mQueueLock) {
mPendingInits.add(transportName);
// TODO: pick a better starting time than now + 1 minute
long delay = 1000 * 60; // one minute, in milliseconds
mAlarmManager.set(AlarmManager.RTC_WAKEUP,
System.currentTimeMillis() + delay, mRunInitIntent);
}
}
} catch (RemoteException e) {
// the transport threw when asked its file naming prefs; declare it invalid
Slog.e(TAG, "Unable to register transport as " + name);
mTransportNames.remove(component);
mTransports.remove(name);
}
| void | removeEverBackedUp(java.lang.String packageName)
if (DEBUG) Slog.v(TAG, "Removing backed-up knowledge of " + packageName);
if (MORE_DEBUG) Slog.v(TAG, "New set:");
synchronized (mEverStoredApps) {
// Rewrite the file and rename to overwrite. If we reboot in the middle,
// we'll recognize on initialization time that the package no longer
// exists and fix it up then.
File tempKnownFile = new File(mBaseStateDir, "processed.new");
RandomAccessFile known = null;
try {
known = new RandomAccessFile(tempKnownFile, "rws");
mEverStoredApps.remove(packageName);
for (String s : mEverStoredApps) {
known.writeUTF(s);
if (MORE_DEBUG) Slog.v(TAG, " " + s);
}
known.close();
known = null;
if (!tempKnownFile.renameTo(mEverStored)) {
throw new IOException("Can't rename " + tempKnownFile + " to " + mEverStored);
}
} catch (IOException e) {
// Bad: we couldn't create the new copy. For safety's sake we
// abandon the whole process and remove all what's-backed-up
// state entirely, meaning we'll force a backup pass for every
// participant on the next boot or [re]install.
Slog.w(TAG, "Error rewriting " + mEverStored, e);
mEverStoredApps.clear();
tempKnownFile.delete();
mEverStored.delete();
} finally {
try { if (known != null) known.close(); } catch (IOException e) {}
}
}
| private void | removePackageFromSetLocked(java.util.HashSet set, java.lang.String packageName)
if (set.contains(packageName)) {
// Found it. Remove this one package from the bookkeeping, and
// if it's the last participating app under this uid we drop the
// (now-empty) set as well.
// Note that we deliberately leave it 'known' in the "ever backed up"
// bookkeeping so that its current-dataset data will be retrieved
// if the app is subsequently reinstalled
if (MORE_DEBUG) Slog.v(TAG, " removing participant " + packageName);
set.remove(packageName);
mPendingBackups.remove(packageName);
}
| void | removePackageParticipantsLocked(java.lang.String[] packageNames, int oldUid)
if (packageNames == null) {
Slog.w(TAG, "removePackageParticipants with null list");
return;
}
if (MORE_DEBUG) Slog.v(TAG, "removePackageParticipantsLocked: uid=" + oldUid
+ " #" + packageNames.length);
for (String pkg : packageNames) {
// Known previous UID, so we know which package set to check
HashSet<String> set = mBackupParticipants.get(oldUid);
if (set != null && set.contains(pkg)) {
removePackageFromSetLocked(set, pkg);
if (set.isEmpty()) {
if (MORE_DEBUG) Slog.v(TAG, " last one of this uid; purging set");
mBackupParticipants.remove(oldUid);
}
}
}
| void | resetBackupState(java.io.File stateFileDir)
synchronized (mQueueLock) {
// Wipe the "what we've ever backed up" tracking
mEverStoredApps.clear();
mEverStored.delete();
mCurrentToken = 0;
writeRestoreTokens();
// Remove all the state files
for (File sf : stateFileDir.listFiles()) {
// ... but don't touch the needs-init sentinel
if (!sf.getName().equals(INIT_SENTINEL_FILE_NAME)) {
sf.delete();
}
}
}
// Enqueue a new backup of every participant
synchronized (mBackupParticipants) {
final int N = mBackupParticipants.size();
for (int i=0; i<N; i++) {
HashSet<String> participants = mBackupParticipants.valueAt(i);
if (participants != null) {
for (String packageName : participants) {
dataChangedImpl(packageName);
}
}
}
}
| public void | restoreAtInstall(java.lang.String packageName, int token)
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
+ " attemping install-time restore");
return;
}
boolean skip = false;
long restoreSet = getAvailableRestoreToken(packageName);
if (DEBUG) Slog.v(TAG, "restoreAtInstall pkg=" + packageName
+ " token=" + Integer.toHexString(token)
+ " restoreSet=" + Long.toHexString(restoreSet));
if (restoreSet == 0) {
if (MORE_DEBUG) Slog.i(TAG, "No restore set");
skip = true;
}
// Do we have a transport to fetch data for us?
IBackupTransport transport = getTransport(mCurrentTransport);
if (transport == null) {
if (DEBUG) Slog.w(TAG, "No transport");
skip = true;
}
if (!mAutoRestore) {
if (DEBUG) {
Slog.w(TAG, "Non-restorable state: auto=" + mAutoRestore);
}
skip = true;
}
if (!skip) {
try {
// okay, we're going to attempt a restore of this package from this restore set.
// The eventual message back into the Package Manager to run the post-install
// steps for 'token' will be issued from the restore handling code.
// This can throw and so *must* happen before the wakelock is acquired
String dirName = transport.transportDirName();
// We can use a synthetic PackageInfo here because:
// 1. We know it's valid, since the Package Manager supplied the name
// 2. Only the packageName field will be used by the restore code
PackageInfo pkg = new PackageInfo();
pkg.packageName = packageName;
mWakelock.acquire();
if (MORE_DEBUG) {
Slog.d(TAG, "Restore at install of " + packageName);
}
Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
msg.obj = new RestoreParams(transport, dirName, null,
restoreSet, pkg, token);
mBackupHandler.sendMessage(msg);
} catch (RemoteException e) {
// Binding to the transport broke; back off and proceed with the installation.
Slog.e(TAG, "Unable to contact transport");
skip = true;
}
}
if (skip) {
// Auto-restore disabled or no way to attempt a restore; just tell the Package
// Manager to proceed with the post-install handling for this package.
if (DEBUG) Slog.v(TAG, "Finishing install immediately");
try {
mPackageManagerBinder.finishPackageInstall(token);
} catch (RemoteException e) { /* can't happen */ }
}
| void | restoreWidgetData(java.lang.String packageName, byte[] widgetData)
// Apply the restored widget state and generate the ID update for the app
AppWidgetBackupBridge.restoreWidgetState(packageName, widgetData, UserHandle.USER_OWNER);
| private void | routeSocketDataToOutput(android.os.ParcelFileDescriptor inPipe, java.io.OutputStream out)
FileInputStream raw = new FileInputStream(inPipe.getFileDescriptor());
DataInputStream in = new DataInputStream(raw);
byte[] buffer = new byte[32 * 1024];
int chunkTotal;
while ((chunkTotal = in.readInt()) > 0) {
while (chunkTotal > 0) {
int toRead = (chunkTotal > buffer.length) ? buffer.length : chunkTotal;
int nRead = in.read(buffer, 0, toRead);
out.write(buffer, 0, nRead);
chunkTotal -= nRead;
}
}
| void | scheduleNextFullBackupJob()Schedule a job to tell us when it's a good time to run a full backup
synchronized (mQueueLock) {
if (mFullBackupQueue.size() > 0) {
// schedule the next job at the point in the future when the least-recently
// backed up app comes due for backup again; or immediately if it's already
// due.
long upcomingLastBackup = mFullBackupQueue.get(0).lastBackup;
long timeSinceLast = System.currentTimeMillis() - upcomingLastBackup;
final long latency = (timeSinceLast < MIN_FULL_BACKUP_INTERVAL)
? (MIN_FULL_BACKUP_INTERVAL - timeSinceLast) : 0;
Runnable r = new Runnable() {
@Override public void run() {
FullBackupJob.schedule(mContext, latency);
}
};
mBackupHandler.postDelayed(r, 2500);
} else {
if (DEBUG_SCHEDULING) {
Slog.i(TAG, "Full backup queue empty; not scheduling");
}
}
}
| public java.lang.String | selectBackupTransport(java.lang.String transport)
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"selectBackupTransport");
synchronized (mTransports) {
final long oldId = Binder.clearCallingIdentity();
try {
String prevTransport = mCurrentTransport;
mCurrentTransport = transport;
Settings.Secure.putString(mContext.getContentResolver(),
Settings.Secure.BACKUP_TRANSPORT, transport);
Slog.v(TAG, "selectBackupTransport() set " + mCurrentTransport
+ " returning " + prevTransport);
return prevTransport;
} finally {
Binder.restoreCallingIdentity(oldId);
}
}
| public void | setAutoRestore(boolean doAutoRestore)
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"setAutoRestore");
Slog.i(TAG, "Auto restore => " + doAutoRestore);
final long oldId = Binder.clearCallingIdentity();
try {
synchronized (this) {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0);
mAutoRestore = doAutoRestore;
}
} finally {
Binder.restoreCallingIdentity(oldId);
}
| public void | setBackupEnabled(boolean enable)
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"setBackupEnabled");
Slog.i(TAG, "Backup enabled => " + enable);
long oldId = Binder.clearCallingIdentity();
try {
boolean wasEnabled = mEnabled;
synchronized (this) {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.BACKUP_ENABLED, enable ? 1 : 0);
mEnabled = enable;
}
synchronized (mQueueLock) {
if (enable && !wasEnabled && mProvisioned) {
// if we've just been enabled, start scheduling backup passes
startBackupAlarmsLocked(BACKUP_INTERVAL);
scheduleNextFullBackupJob();
} else if (!enable) {
// No longer enabled, so stop running backups
if (DEBUG) Slog.i(TAG, "Opting out of backup");
mAlarmManager.cancel(mRunBackupIntent);
// This also constitutes an opt-out, so we wipe any data for
// this device from the backend. We start that process with
// an alarm in order to guarantee wakelock states.
if (wasEnabled && mProvisioned) {
// NOTE: we currently flush every registered transport, not just
// the currently-active one.
HashSet<String> allTransports;
synchronized (mTransports) {
allTransports = new HashSet<String>(mTransports.keySet());
}
// build the set of transports for which we are posting an init
for (String transport : allTransports) {
recordInitPendingLocked(true, transport);
}
mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
mRunInitIntent);
}
}
}
} finally {
Binder.restoreCallingIdentity(oldId);
}
| public boolean | setBackupPassword(java.lang.String currentPw, java.lang.String newPw)
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"setBackupPassword");
// When processing v1 passwords we may need to try two different PBKDF2 checksum regimes
final boolean pbkdf2Fallback = (mPasswordVersion < BACKUP_PW_FILE_VERSION);
// If the supplied pw doesn't hash to the the saved one, fail. The password
// might be caught in the legacy crypto mismatch; verify that too.
if (!passwordMatchesSaved(PBKDF_CURRENT, currentPw, PBKDF2_HASH_ROUNDS)
&& !(pbkdf2Fallback && passwordMatchesSaved(PBKDF_FALLBACK,
currentPw, PBKDF2_HASH_ROUNDS))) {
return false;
}
// Snap up to current on the pw file version
mPasswordVersion = BACKUP_PW_FILE_VERSION;
FileOutputStream pwFout = null;
DataOutputStream pwOut = null;
try {
pwFout = new FileOutputStream(mPasswordVersionFile);
pwOut = new DataOutputStream(pwFout);
pwOut.writeInt(mPasswordVersion);
} catch (IOException e) {
Slog.e(TAG, "Unable to write backup pw version; password not changed");
return false;
} finally {
try {
if (pwOut != null) pwOut.close();
if (pwFout != null) pwFout.close();
} catch (IOException e) {
Slog.w(TAG, "Unable to close pw version record");
}
}
// Clearing the password is okay
if (newPw == null || newPw.isEmpty()) {
if (mPasswordHashFile.exists()) {
if (!mPasswordHashFile.delete()) {
// Unable to delete the old pw file, so fail
Slog.e(TAG, "Unable to clear backup password");
return false;
}
}
mPasswordHash = null;
mPasswordSalt = null;
return true;
}
try {
// Okay, build the hash of the new backup password
byte[] salt = randomBytes(PBKDF2_SALT_SIZE);
String newPwHash = buildPasswordHash(PBKDF_CURRENT, newPw, salt, PBKDF2_HASH_ROUNDS);
OutputStream pwf = null, buffer = null;
DataOutputStream out = null;
try {
pwf = new FileOutputStream(mPasswordHashFile);
buffer = new BufferedOutputStream(pwf);
out = new DataOutputStream(buffer);
// integer length of the salt array, followed by the salt,
// then the hex pw hash string
out.writeInt(salt.length);
out.write(salt);
out.writeUTF(newPwHash);
out.flush();
mPasswordHash = newPwHash;
mPasswordSalt = salt;
return true;
} finally {
if (out != null) out.close();
if (buffer != null) buffer.close();
if (pwf != null) pwf.close();
}
} catch (IOException e) {
Slog.e(TAG, "Unable to set backup password");
}
return false;
| public void | setBackupProvisioned(boolean available)
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"setBackupProvisioned");
/*
* This is now a no-op; provisioning is simply the device's own setup state.
*/
| void | signalFullBackupRestoreCompletion(com.android.server.backup.BackupManagerService$FullParams params)
synchronized (params.latch) {
params.latch.set(true);
params.latch.notifyAll();
}
| static boolean | signaturesMatch(java.util.ArrayList storedSigHashes, android.content.pm.PackageInfo target)
if (target == null) {
return false;
}
// If the target resides on the system partition, we allow it to restore
// data from the like-named package in a restore set even if the signatures
// do not match. (Unlike general applications, those flashed to the system
// partition will be signed with the device's platform certificate, so on
// different phones the same system app will have different signatures.)
if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
if (DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check");
return true;
}
// Allow unsigned apps, but not signed on one device and unsigned on the other
// !!! TODO: is this the right policy?
Signature[] deviceSigs = target.signatures;
if (MORE_DEBUG) Slog.v(TAG, "signaturesMatch(): stored=" + storedSigHashes
+ " device=" + deviceSigs);
if ((storedSigHashes == null || storedSigHashes.size() == 0)
&& (deviceSigs == null || deviceSigs.length == 0)) {
return true;
}
if (storedSigHashes == null || deviceSigs == null) {
return false;
}
// !!! TODO: this demands that every stored signature match one
// that is present on device, and does not demand the converse.
// Is this this right policy?
final int nStored = storedSigHashes.size();
final int nDevice = deviceSigs.length;
// hash each on-device signature
ArrayList<byte[]> deviceHashes = new ArrayList<byte[]>(nDevice);
for (int i = 0; i < nDevice; i++) {
deviceHashes.add(hashSignature(deviceSigs[i]));
}
// now ensure that each stored sig (hash) matches an on-device sig (hash)
for (int n = 0; n < nStored; n++) {
boolean match = false;
final byte[] storedHash = storedSigHashes.get(n);
for (int i = 0; i < nDevice; i++) {
if (Arrays.equals(storedHash, deviceHashes.get(i))) {
match = true;
break;
}
}
// match is false when no on-device sig matched one of the stored ones
if (!match) {
return false;
}
}
return true;
| static boolean | signaturesMatch(android.content.pm.Signature[] storedSigs, android.content.pm.PackageInfo target)
if (target == null) {
return false;
}
// If the target resides on the system partition, we allow it to restore
// data from the like-named package in a restore set even if the signatures
// do not match. (Unlike general applications, those flashed to the system
// partition will be signed with the device's platform certificate, so on
// different phones the same system app will have different signatures.)
if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
if (DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check");
return true;
}
// Allow unsigned apps, but not signed on one device and unsigned on the other
// !!! TODO: is this the right policy?
Signature[] deviceSigs = target.signatures;
if (MORE_DEBUG) Slog.v(TAG, "signaturesMatch(): stored=" + storedSigs
+ " device=" + deviceSigs);
if ((storedSigs == null || storedSigs.length == 0)
&& (deviceSigs == null || deviceSigs.length == 0)) {
return true;
}
if (storedSigs == null || deviceSigs == null) {
return false;
}
// !!! TODO: this demands that every stored signature match one
// that is present on device, and does not demand the converse.
// Is this this right policy?
int nStored = storedSigs.length;
int nDevice = deviceSigs.length;
for (int i=0; i < nStored; i++) {
boolean match = false;
for (int j=0; j < nDevice; j++) {
if (storedSigs[i].equals(deviceSigs[j])) {
match = true;
break;
}
}
if (!match) {
return false;
}
}
return true;
| private void | startBackupAlarmsLocked(long delayBeforeFirstBackup)
// We used to use setInexactRepeating(), but that may be linked to
// backups running at :00 more often than not, creating load spikes.
// Schedule at an exact time for now, and also add a bit of "fuzz".
Random random = new Random();
long when = System.currentTimeMillis() + delayBeforeFirstBackup +
random.nextInt(FUZZ_MILLIS);
mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP, when,
BACKUP_INTERVAL + random.nextInt(FUZZ_MILLIS), mRunBackupIntent);
mNextBackupPass = when;
| void | startConfirmationTimeout(int token, com.android.server.backup.BackupManagerService$FullParams params)
if (MORE_DEBUG) Slog.d(TAG, "Posting conf timeout msg after "
+ TIMEOUT_FULL_CONFIRMATION + " millis");
Message msg = mBackupHandler.obtainMessage(MSG_FULL_CONFIRMATION_TIMEOUT,
token, 0, params);
mBackupHandler.sendMessageDelayed(msg, TIMEOUT_FULL_CONFIRMATION);
| boolean | startConfirmationUi(int token, java.lang.String action)
try {
Intent confIntent = new Intent(action);
confIntent.setClassName("com.android.backupconfirm",
"com.android.backupconfirm.BackupRestoreConfirmation");
confIntent.putExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, token);
confIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(confIntent);
} catch (ActivityNotFoundException e) {
return false;
}
return true;
| boolean | tryBindTransport(android.content.pm.ServiceInfo info)
try {
PackageInfo packInfo = mPackageManager.getPackageInfo(info.packageName, 0);
if ((packInfo.applicationInfo.flags & ApplicationInfo.FLAG_PRIVILEGED) != 0) {
return bindTransport(info);
} else {
Slog.w(TAG, "Transport package " + info.packageName + " not privileged");
}
} catch (NameNotFoundException e) {
Slog.w(TAG, "Problem resolving transport package " + info.packageName);
}
return false;
| void | waitForCompletion(com.android.server.backup.BackupManagerService$FullParams params)
synchronized (params.latch) {
while (params.latch.get() == false) {
try {
params.latch.wait();
} catch (InterruptedException e) { /* never interrupted */ }
}
}
| boolean | waitUntilOperationComplete(int token)
if (MORE_DEBUG) Slog.i(TAG, "Blocking until operation complete for "
+ Integer.toHexString(token));
int finalState = OP_PENDING;
Operation op = null;
synchronized (mCurrentOpLock) {
while (true) {
op = mCurrentOperations.get(token);
if (op == null) {
// mysterious disappearance: treat as success with no callback
break;
} else {
if (op.state == OP_PENDING) {
try {
mCurrentOpLock.wait();
} catch (InterruptedException e) {}
// When the wait is notified we loop around and recheck the current state
} else {
// No longer pending; we're done
finalState = op.state;
break;
}
}
}
}
mBackupHandler.removeMessages(MSG_TIMEOUT);
if (MORE_DEBUG) Slog.v(TAG, "operation " + Integer.toHexString(token)
+ " complete: finalState=" + finalState);
return finalState == OP_ACKNOWLEDGED;
| private void | writeFullBackupScheduleAsync()
mBackupHandler.removeCallbacks(mFullBackupScheduleWriter);
mBackupHandler.post(mFullBackupScheduleWriter);
| void | writeRestoreTokens()
try {
RandomAccessFile af = new RandomAccessFile(mTokenFile, "rwd");
// First, the version number of this record, for futureproofing
af.writeInt(CURRENT_ANCESTRAL_RECORD_VERSION);
// Write the ancestral and current tokens
af.writeLong(mAncestralToken);
af.writeLong(mCurrentToken);
// Now write the set of ancestral packages
if (mAncestralPackages == null) {
af.writeInt(-1);
} else {
af.writeInt(mAncestralPackages.size());
if (DEBUG) Slog.v(TAG, "Ancestral packages: " + mAncestralPackages.size());
for (String pkgName : mAncestralPackages) {
af.writeUTF(pkgName);
if (MORE_DEBUG) Slog.v(TAG, " " + pkgName);
}
}
af.close();
} catch (IOException e) {
Slog.w(TAG, "Unable to write token file:", e);
}
| private void | writeToJournalLocked(java.lang.String str)
RandomAccessFile out = null;
try {
if (mJournal == null) mJournal = File.createTempFile("journal", null, mJournalDir);
out = new RandomAccessFile(mJournal, "rws");
out.seek(out.length());
out.writeUTF(str);
} catch (IOException e) {
Slog.e(TAG, "Can't write " + str + " to backup journal", e);
mJournal = null;
} finally {
try { if (out != null) out.close(); } catch (IOException e) {}
}
|
|