Merge "API to select backup transport"
This commit is contained in:
committed by
Android (Google) Code Review
commit
a393ff7dc7
@@ -107,6 +107,7 @@ LOCAL_SRC_FILES += \
|
||||
core/java/android/app/backup/IFullBackupRestoreObserver.aidl \
|
||||
core/java/android/app/backup/IRestoreObserver.aidl \
|
||||
core/java/android/app/backup/IRestoreSession.aidl \
|
||||
core/java/android/app/backup/ISelectBackupTransportCallback.aidl \
|
||||
core/java/android/app/usage/IStorageStatsManager.aidl \
|
||||
core/java/android/app/usage/IUsageStatsManager.aidl \
|
||||
core/java/android/bluetooth/IBluetooth.aidl \
|
||||
|
||||
@@ -6787,15 +6787,18 @@ package android.app.backup {
|
||||
method public int requestBackup(java.lang.String[], android.app.backup.BackupObserver);
|
||||
method public int requestBackup(java.lang.String[], android.app.backup.BackupObserver, int);
|
||||
method public int requestRestore(android.app.backup.RestoreObserver);
|
||||
method public java.lang.String selectBackupTransport(java.lang.String);
|
||||
method public deprecated java.lang.String selectBackupTransport(java.lang.String);
|
||||
method public void selectBackupTransport(android.content.ComponentName, android.app.backup.SelectBackupTransportCallback);
|
||||
method public void setAutoRestore(boolean);
|
||||
method public void setBackupEnabled(boolean);
|
||||
field public static final int ERROR_AGENT_FAILURE = -1003; // 0xfffffc15
|
||||
field public static final int ERROR_BACKUP_NOT_ALLOWED = -2001; // 0xfffff82f
|
||||
field public static final int ERROR_PACKAGE_NOT_FOUND = -2002; // 0xfffff82e
|
||||
field public static final int ERROR_TRANSPORT_ABORTED = -1000; // 0xfffffc18
|
||||
field public static final int ERROR_TRANSPORT_INVALID = -2; // 0xfffffffe
|
||||
field public static final int ERROR_TRANSPORT_PACKAGE_REJECTED = -1002; // 0xfffffc16
|
||||
field public static final int ERROR_TRANSPORT_QUOTA_EXCEEDED = -1005; // 0xfffffc13
|
||||
field public static final int ERROR_TRANSPORT_UNAVAILABLE = -1; // 0xffffffff
|
||||
field public static final int FLAG_NON_INCREMENTAL_BACKUP = 1; // 0x1
|
||||
field public static final java.lang.String PACKAGE_MANAGER_SENTINEL = "@pm@";
|
||||
field public static final int SUCCESS = 0; // 0x0
|
||||
@@ -6910,6 +6913,12 @@ package android.app.backup {
|
||||
field public long token;
|
||||
}
|
||||
|
||||
public abstract class SelectBackupTransportCallback {
|
||||
ctor public SelectBackupTransportCallback();
|
||||
method public void onFailure(int);
|
||||
method public void onSuccess(java.lang.String);
|
||||
}
|
||||
|
||||
public class SharedPreferencesBackupHelper extends android.app.backup.FileBackupHelperBase implements android.app.backup.BackupHelper {
|
||||
ctor public SharedPreferencesBackupHelper(android.content.Context, java.lang.String...);
|
||||
method public void performBackup(android.os.ParcelFileDescriptor, android.app.backup.BackupDataOutput, android.os.ParcelFileDescriptor);
|
||||
|
||||
@@ -18,20 +18,24 @@ package com.android.commands.bmgr;
|
||||
|
||||
import android.app.backup.BackupManager;
|
||||
import android.app.backup.BackupProgress;
|
||||
import android.app.backup.RestoreSet;
|
||||
import android.app.backup.IBackupManager;
|
||||
import android.app.backup.IBackupObserver;
|
||||
import android.app.backup.IRestoreObserver;
|
||||
import android.app.backup.IRestoreSession;
|
||||
import android.app.backup.RestoreSet;
|
||||
import android.app.backup.ISelectBackupTransportCallback;
|
||||
import android.content.ComponentName;
|
||||
import android.content.pm.IPackageManager;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.UserHandle;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
public final class Bmgr {
|
||||
IBackupManager mBmgr;
|
||||
@@ -363,6 +367,11 @@ public final class Bmgr {
|
||||
return;
|
||||
}
|
||||
|
||||
if ("-c".equals(which)) {
|
||||
doTransportByComponent();
|
||||
return;
|
||||
}
|
||||
|
||||
String old = mBmgr.selectBackupTransport(which);
|
||||
if (old == null) {
|
||||
System.out.println("Unknown transport '" + which
|
||||
@@ -370,12 +379,50 @@ public final class Bmgr {
|
||||
} else {
|
||||
System.out.println("Selected transport " + which + " (formerly " + old + ")");
|
||||
}
|
||||
|
||||
} catch (RemoteException e) {
|
||||
System.err.println(e.toString());
|
||||
System.err.println(BMGR_NOT_RUNNING_ERR);
|
||||
}
|
||||
}
|
||||
|
||||
private void doTransportByComponent() {
|
||||
String which = nextArg();
|
||||
if (which == null) {
|
||||
showUsage();
|
||||
return;
|
||||
}
|
||||
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
|
||||
try {
|
||||
mBmgr.selectBackupTransportAsync(ComponentName.unflattenFromString(which),
|
||||
new ISelectBackupTransportCallback.Stub() {
|
||||
@Override
|
||||
public void onSuccess(String transportName) {
|
||||
System.out.println("Success. Selected transport: " + transportName);
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(int reason) {
|
||||
System.err.println("Failure. error=" + reason);
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
} catch (RemoteException e) {
|
||||
System.err.println(e.toString());
|
||||
System.err.println(BMGR_NOT_RUNNING_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
latch.await();
|
||||
} catch (InterruptedException e) {
|
||||
System.err.println("Operation interrupted.");
|
||||
}
|
||||
}
|
||||
|
||||
private void doWipe() {
|
||||
String transport = nextArg();
|
||||
if (transport == null) {
|
||||
@@ -427,7 +474,16 @@ public final class Bmgr {
|
||||
}
|
||||
|
||||
private void doListTransports() {
|
||||
String arg = nextArg();
|
||||
|
||||
try {
|
||||
if ("-c".equals(arg)) {
|
||||
for (ComponentName transport : mBmgr.listAllTransportComponents()) {
|
||||
System.out.println(transport.flattenToShortString());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
String current = mBmgr.getCurrentTransport();
|
||||
String[] transports = mBmgr.listAllTransports();
|
||||
if (transports == null || transports.length == 0) {
|
||||
@@ -649,9 +705,9 @@ public final class Bmgr {
|
||||
System.err.println(" bmgr backup PACKAGE");
|
||||
System.err.println(" bmgr enable BOOL");
|
||||
System.err.println(" bmgr enabled");
|
||||
System.err.println(" bmgr list transports");
|
||||
System.err.println(" bmgr list transports [-c]");
|
||||
System.err.println(" bmgr list sets");
|
||||
System.err.println(" bmgr transport WHICH");
|
||||
System.err.println(" bmgr transport WHICH|-c WHICH_COMPONENT");
|
||||
System.err.println(" bmgr restore TOKEN");
|
||||
System.err.println(" bmgr restore TOKEN PACKAGE...");
|
||||
System.err.println(" bmgr restore PACKAGE");
|
||||
@@ -673,15 +729,18 @@ public final class Bmgr {
|
||||
System.err.println("the backup mechanism.");
|
||||
System.err.println("");
|
||||
System.err.println("The 'list transports' command reports the names of the backup transports");
|
||||
System.err.println("currently available on the device. These names can be passed as arguments");
|
||||
System.err.println("BackupManager is currently bound to. These names can be passed as arguments");
|
||||
System.err.println("to the 'transport' and 'wipe' commands. The currently active transport");
|
||||
System.err.println("is indicated with a '*' character.");
|
||||
System.err.println("is indicated with a '*' character. If -c flag is used, all available");
|
||||
System.err.println("transport components on the device are listed. These can be used with");
|
||||
System.err.println("the component variant of 'transport' command.");
|
||||
System.err.println("");
|
||||
System.err.println("The 'list sets' command reports the token and name of each restore set");
|
||||
System.err.println("available to the device via the currently active transport.");
|
||||
System.err.println("");
|
||||
System.err.println("The 'transport' command designates the named transport as the currently");
|
||||
System.err.println("active one. This setting is persistent across reboots.");
|
||||
System.err.println("active one. This setting is persistent across reboots. If -c flag is");
|
||||
System.err.println("specified, the following string is treated as a component name.");
|
||||
System.err.println("");
|
||||
System.err.println("The 'restore' command when given just a restore token initiates a full-system");
|
||||
System.err.println("restore operation from the currently active transport. It will deliver");
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package android.app.backup;
|
||||
|
||||
import android.annotation.SystemApi;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
@@ -157,6 +158,25 @@ public class BackupManager {
|
||||
@SystemApi
|
||||
public static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
|
||||
|
||||
|
||||
/**
|
||||
* This error code is passed to {@link SelectBackupTransportCallback#onFailure(int)}
|
||||
* if the requested transport is unavailable.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public static final int ERROR_TRANSPORT_UNAVAILABLE = -1;
|
||||
|
||||
/**
|
||||
* This error code is passed to {@link SelectBackupTransportCallback#onFailure(int)} if the
|
||||
* requested transport is not a valid BackupTransport.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public static final int ERROR_TRANSPORT_INVALID = -2;
|
||||
|
||||
private Context mContext;
|
||||
private static IBackupManager sService;
|
||||
|
||||
@@ -390,17 +410,20 @@ public class BackupManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the current backup transport. Callers must hold the
|
||||
* android.permission.BACKUP permission to use this method.
|
||||
* Specify the current backup transport.
|
||||
*
|
||||
* <p> Callers must hold the android.permission.BACKUP permission to use this method.
|
||||
*
|
||||
* @param transport The name of the transport to select. This should be one
|
||||
* of the names returned by {@link #listAllTransports()}.
|
||||
* of the names returned by {@link #listAllTransports()}. This is the String returned by
|
||||
* {@link BackupTransport#name()} for the particular transport.
|
||||
* @return The name of the previously selected transport. If the given transport
|
||||
* name is not one of the currently available transports, no change is made to
|
||||
* the current transport setting and the method returns null.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@Deprecated
|
||||
@SystemApi
|
||||
public String selectBackupTransport(String transport) {
|
||||
checkServiceBinder();
|
||||
@@ -414,6 +437,34 @@ public class BackupManager {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the current backup transport and get notified when the transport is ready to be used.
|
||||
* This method is async because BackupManager might need to bind to the specified transport
|
||||
* which is in a separate process.
|
||||
*
|
||||
* <p>Callers must hold the android.permission.BACKUP permission to use this method.
|
||||
*
|
||||
* @param transport ComponentName of the service hosting the transport. This is different from
|
||||
* the transport's name that is returned by {@link BackupTransport#name()}.
|
||||
* @param listener A listener object to get a callback on the transport being selected.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public void selectBackupTransport(ComponentName transport,
|
||||
SelectBackupTransportCallback listener) {
|
||||
checkServiceBinder();
|
||||
if (sService != null) {
|
||||
try {
|
||||
SelectTransportListenerWrapper wrapper = listener == null ?
|
||||
null : new SelectTransportListenerWrapper(mContext, listener);
|
||||
sService.selectBackupTransportAsync(transport, wrapper);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "selectBackupTransportAsync() couldn't connect");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule an immediate backup attempt for all pending key/value updates. This
|
||||
* is primarily intended for transports to use when they detect a suitable
|
||||
@@ -598,4 +649,35 @@ public class BackupManager {
|
||||
mHandler.obtainMessage(MSG_FINISHED, status, 0));
|
||||
}
|
||||
}
|
||||
|
||||
private class SelectTransportListenerWrapper extends ISelectBackupTransportCallback.Stub {
|
||||
|
||||
private final Handler mHandler;
|
||||
private final SelectBackupTransportCallback mListener;
|
||||
|
||||
SelectTransportListenerWrapper(Context context, SelectBackupTransportCallback listener) {
|
||||
mHandler = new Handler(context.getMainLooper());
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(final String transportName) {
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mListener.onSuccess(transportName);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final int reason) {
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mListener.onFailure(reason);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,8 +19,10 @@ package android.app.backup;
|
||||
import android.app.backup.IBackupObserver;
|
||||
import android.app.backup.IFullBackupRestoreObserver;
|
||||
import android.app.backup.IRestoreSession;
|
||||
import android.app.backup.ISelectBackupTransportCallback;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.content.Intent;
|
||||
import android.content.ComponentName;
|
||||
|
||||
/**
|
||||
* Direct interface to the Backup Manager Service that applications invoke on. The only
|
||||
@@ -217,6 +219,8 @@ interface IBackupManager {
|
||||
*/
|
||||
String[] listAllTransports();
|
||||
|
||||
ComponentName[] listAllTransportComponents();
|
||||
|
||||
/**
|
||||
* Retrieve the list of whitelisted transport components. Callers do </i>not</i> need
|
||||
* any special permission.
|
||||
@@ -237,6 +241,21 @@ interface IBackupManager {
|
||||
*/
|
||||
String selectBackupTransport(String transport);
|
||||
|
||||
/**
|
||||
* Specify the current backup transport and get notified when the transport is ready to be used.
|
||||
* This method is async because BackupManager might need to bind to the specified transport
|
||||
* which is in a separate process.
|
||||
*
|
||||
* <p>Callers must hold the android.permission.BACKUP permission to use this method.
|
||||
*
|
||||
* @param transport ComponentName of the service hosting the transport. This is different from
|
||||
* the transport's name that is returned by {@link BackupTransport#name()}.
|
||||
* @param listener A listener object to get a callback on the transport being selected.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
void selectBackupTransportAsync(in ComponentName transport, ISelectBackupTransportCallback listener);
|
||||
|
||||
/**
|
||||
* Get the configuration Intent, if any, from the given transport. Callers must
|
||||
* hold the android.permission.BACKUP permission in order to use this method.
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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 android.app.backup;
|
||||
|
||||
/**
|
||||
* Callback class for receiving success or failure callbacks on selecting a backup transport. These
|
||||
* methods will all be called on your application's main thread.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
oneway interface ISelectBackupTransportCallback {
|
||||
|
||||
/**
|
||||
* Called when BackupManager has successfully bound to the requested transport.
|
||||
*
|
||||
* @param transportName Name of the selected transport. This is the String returned by
|
||||
* {@link BackupTransport#name()}.
|
||||
*/
|
||||
void onSuccess(String transportName);
|
||||
|
||||
/**
|
||||
* Called when BackupManager fails to bind to the requested transport.
|
||||
*
|
||||
* @param reason Error code denoting reason for failure.
|
||||
*/
|
||||
void onFailure(int reason);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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 android.app.backup;
|
||||
|
||||
import android.annotation.SystemApi;
|
||||
|
||||
/**
|
||||
* Callback class for receiving success or failure callbacks on selecting a backup transport. These
|
||||
* methods will all be called on your application's main thread.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public abstract class SelectBackupTransportCallback {
|
||||
|
||||
/**
|
||||
* Called when BackupManager has successfully bound to the requested transport.
|
||||
*
|
||||
* @param transportName Name of the selected transport. This is the String returned by
|
||||
* {@link BackupTransport#name()}.
|
||||
*/
|
||||
public void onSuccess(String transportName){}
|
||||
|
||||
/**
|
||||
* Called when BackupManager fails to bind to the requested transport.
|
||||
*
|
||||
* @param reason Error code denoting reason for failure.
|
||||
*/
|
||||
public void onFailure(int reason){}
|
||||
}
|
||||
@@ -34,13 +34,15 @@ import android.app.backup.BackupProgress;
|
||||
import android.app.backup.BackupTransport;
|
||||
import android.app.backup.FullBackup;
|
||||
import android.app.backup.FullBackupDataOutput;
|
||||
import android.app.backup.IBackupObserver;
|
||||
import android.app.backup.RestoreDescription;
|
||||
import android.app.backup.RestoreSet;
|
||||
import android.app.backup.IBackupManager;
|
||||
import android.app.backup.IBackupObserver;
|
||||
import android.app.backup.IFullBackupRestoreObserver;
|
||||
import android.app.backup.IRestoreObserver;
|
||||
import android.app.backup.IRestoreSession;
|
||||
import android.app.backup.ISelectBackupTransportCallback;
|
||||
import android.app.backup.RestoreDescription;
|
||||
import android.app.backup.RestoreSet;
|
||||
import android.app.backup.SelectBackupTransportCallback;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
@@ -55,16 +57,15 @@ import android.content.pm.IPackageDeleteObserver;
|
||||
import android.content.pm.IPackageManager;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.content.pm.Signature;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.pm.Signature;
|
||||
import android.database.ContentObserver;
|
||||
import android.net.Uri;
|
||||
import android.os.Binder;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.os.Environment.UserEnvironment;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.IBinder;
|
||||
@@ -79,15 +80,12 @@ import android.os.ServiceManager;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserHandle;
|
||||
import android.os.WorkSource;
|
||||
import android.os.Environment.UserEnvironment;
|
||||
import android.os.storage.IStorageManager;
|
||||
import android.os.storage.StorageManager;
|
||||
import android.provider.Settings;
|
||||
import android.system.ErrnoException;
|
||||
import android.system.Os;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.AtomicFile;
|
||||
import android.util.EventLog;
|
||||
import android.util.Log;
|
||||
@@ -105,6 +103,8 @@ import com.android.server.SystemConfig;
|
||||
import com.android.server.SystemService;
|
||||
import com.android.server.backup.PackageManagerBackupAgent.Metadata;
|
||||
|
||||
import libcore.io.IoUtils;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
@@ -139,7 +139,6 @@ import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Objects;
|
||||
import java.util.Random;
|
||||
@@ -166,8 +165,6 @@ import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.PBEKeySpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
import libcore.io.IoUtils;
|
||||
|
||||
public class BackupManagerService {
|
||||
|
||||
private static final String TAG = "BackupManagerService";
|
||||
@@ -271,6 +268,8 @@ public class BackupManagerService {
|
||||
private IStorageManager mStorageManager;
|
||||
IBackupManager mBackupManagerBinder;
|
||||
|
||||
private final TransportManager mTransportManager;
|
||||
|
||||
boolean mEnabled; // access to this is synchronized on 'this'
|
||||
boolean mProvisioned;
|
||||
boolean mAutoRestore;
|
||||
@@ -322,16 +321,6 @@ public class BackupManagerService {
|
||||
final Object mClearDataLock = new Object();
|
||||
volatile boolean mClearingData;
|
||||
|
||||
// Transport bookkeeping
|
||||
final ArraySet<ComponentName> mTransportWhitelist;
|
||||
final Intent mTransportServiceIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST);
|
||||
final ArrayMap<String,String> mTransportNames
|
||||
= new ArrayMap<String,String>(); // component name -> registration name
|
||||
final ArrayMap<String,IBackupTransport> mTransports
|
||||
= new ArrayMap<String,IBackupTransport>(); // registration name -> binder
|
||||
final ArrayMap<String,TransportConnection> mTransportConnections
|
||||
= new ArrayMap<String,TransportConnection>();
|
||||
String mCurrentTransport;
|
||||
ActiveRestoreSession mActiveRestoreSession;
|
||||
|
||||
// Watch the device provisioning operation during setup
|
||||
@@ -756,7 +745,7 @@ public class BackupManagerService {
|
||||
{
|
||||
mLastBackupPass = System.currentTimeMillis();
|
||||
|
||||
IBackupTransport transport = getTransport(mCurrentTransport);
|
||||
IBackupTransport transport = mTransportManager.getCurrentTransportBinder();
|
||||
if (transport == null) {
|
||||
Slog.v(TAG, "Backup requested but no transport available");
|
||||
synchronized (mQueueLock) {
|
||||
@@ -1202,32 +1191,19 @@ public class BackupManagerService {
|
||||
// Set up our transport options and initialize the default transport
|
||||
// TODO: Don't create transports that we don't need to?
|
||||
SystemConfig systemConfig = SystemConfig.getInstance();
|
||||
mTransportWhitelist = systemConfig.getBackupTransportWhitelist();
|
||||
Set<ComponentName> transportWhitelist = systemConfig.getBackupTransportWhitelist();
|
||||
|
||||
String transport = Settings.Secure.getString(context.getContentResolver(),
|
||||
Settings.Secure.BACKUP_TRANSPORT);
|
||||
if (TextUtils.isEmpty(transport)) {
|
||||
transport = null;
|
||||
}
|
||||
mCurrentTransport = transport;
|
||||
if (DEBUG) Slog.v(TAG, "Starting with transport " + mCurrentTransport);
|
||||
String currentTransport = transport;
|
||||
if (DEBUG) Slog.v(TAG, "Starting with transport " + currentTransport);
|
||||
|
||||
// Find all transport hosts and bind to their services
|
||||
// TODO: http://b/22388012
|
||||
List<ResolveInfo> hosts = mPackageManager.queryIntentServicesAsUser(
|
||||
mTransportServiceIntent, 0, UserHandle.USER_SYSTEM);
|
||||
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 transportService = hosts.get(i).serviceInfo;
|
||||
if (MORE_DEBUG) {
|
||||
Slog.v(TAG, " " + transportService.packageName + "/" + transportService.name);
|
||||
}
|
||||
tryBindTransport(transportService);
|
||||
}
|
||||
}
|
||||
mTransportManager = new TransportManager(context, transportWhitelist, currentTransport,
|
||||
mTransportBoundListener);
|
||||
mTransportManager.registerAllTransports();
|
||||
|
||||
// Now that we know about valid backup participants, parse any
|
||||
// leftover journal files into the pending backup set
|
||||
@@ -1751,7 +1727,7 @@ public class BackupManagerService {
|
||||
mBackupHandler.removeMessages(MSG_RETRY_INIT);
|
||||
|
||||
try {
|
||||
IBackupTransport transport = getTransport(transportName);
|
||||
IBackupTransport transport = mTransportManager.getTransportBinder(transportName);
|
||||
if (transport != null) {
|
||||
String transportDirName = transport.transportDirName();
|
||||
File stateDir = new File(mBaseStateDir, transportDirName);
|
||||
@@ -1829,49 +1805,39 @@ public class BackupManagerService {
|
||||
}
|
||||
}
|
||||
|
||||
// Add a transport to our set of available backends. If 'transport' is null, this
|
||||
// is an unregistration, and the transport's entry is removed from our bookkeeping.
|
||||
private void registerTransport(String name, String component, 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;
|
||||
}
|
||||
}
|
||||
private TransportManager.TransportBoundListener mTransportBoundListener =
|
||||
new TransportManager.TransportBoundListener() {
|
||||
@Override
|
||||
public boolean onTransportBound(IBackupTransport transport) {
|
||||
// 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.
|
||||
String name = null;
|
||||
try {
|
||||
name = transport.name();
|
||||
String transportDirName = transport.transportDirName();
|
||||
File stateDir = new File(mBaseStateDir, transportDirName);
|
||||
stateDir.mkdirs();
|
||||
|
||||
// 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(name);
|
||||
|
||||
File initSentinel = new File(stateDir, INIT_SENTINEL_FILE_NAME);
|
||||
if (initSentinel.exists()) {
|
||||
synchronized (mQueueLock) {
|
||||
mPendingInits.add(name);
|
||||
|
||||
// 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);
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
// the transport threw when asked its file naming prefs; declare it invalid
|
||||
Slog.w(TAG, "Failed to regiser transport: " + name);
|
||||
return false;
|
||||
}
|
||||
} catch (Exception 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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// ----- Track installation/removal of packages -----
|
||||
BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
|
||||
@@ -1899,75 +1865,17 @@ public class BackupManagerService {
|
||||
|
||||
// At package-changed we only care about looking at new transport states
|
||||
if (changed) {
|
||||
try {
|
||||
String[] components =
|
||||
intent.getStringArrayExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
|
||||
String[] components =
|
||||
intent.getStringArrayExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
|
||||
|
||||
if (MORE_DEBUG) {
|
||||
Slog.i(TAG, "Package " + pkgName + " changed; rechecking");
|
||||
for (int i = 0; i < components.length; i++) {
|
||||
Slog.i(TAG, " * " + components[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// In general we need to try to bind any time we see a component enable
|
||||
// state change, because that change may have made a transport available.
|
||||
// However, because we currently only support a single transport component
|
||||
// per package, we can skip the bind attempt if the change (a) affects a
|
||||
// package known to host a transport, but (b) does not affect the known
|
||||
// transport component itself.
|
||||
//
|
||||
// In addition, if the change *is* to a known transport component, we need
|
||||
// to unbind it before retrying the binding.
|
||||
boolean tryBind = true;
|
||||
synchronized (mTransports) {
|
||||
TransportConnection conn = mTransportConnections.get(pkgName);
|
||||
if (conn != null) {
|
||||
// We have a bound transport in this package; do we need to rebind it?
|
||||
final ServiceInfo svc = conn.mTransport;
|
||||
ComponentName svcName =
|
||||
new ComponentName(svc.packageName, svc.name);
|
||||
if (svc.packageName.equals(pkgName)) {
|
||||
final String className = svcName.getClassName();
|
||||
if (MORE_DEBUG) {
|
||||
Slog.i(TAG, "Checking need to rebind " + className);
|
||||
}
|
||||
// See whether it's the transport component within this package
|
||||
boolean isTransport = false;
|
||||
for (int i = 0; i < components.length; i++) {
|
||||
if (className.equals(components[i])) {
|
||||
// Okay, it's an existing transport component.
|
||||
final String flatName = svcName.flattenToShortString();
|
||||
mContext.unbindService(conn);
|
||||
mTransportConnections.remove(pkgName);
|
||||
mTransports.remove(mTransportNames.get(flatName));
|
||||
mTransportNames.remove(flatName);
|
||||
isTransport = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isTransport) {
|
||||
// A non-transport component within a package that is hosting
|
||||
// a bound transport
|
||||
tryBind = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// and now (re)bind as appropriate
|
||||
if (tryBind) {
|
||||
if (MORE_DEBUG) {
|
||||
Slog.i(TAG, "Yes, need to recheck binding");
|
||||
}
|
||||
PackageInfo app = mPackageManager.getPackageInfo(pkgName, 0);
|
||||
checkForTransportAndBind(app);
|
||||
}
|
||||
} catch (NameNotFoundException e) {
|
||||
// Nope, can't find it - just ignore
|
||||
if (MORE_DEBUG) {
|
||||
Slog.w(TAG, "Can't find changed package " + pkgName);
|
||||
if (MORE_DEBUG) {
|
||||
Slog.i(TAG, "Package " + pkgName + " changed; rechecking");
|
||||
for (int i = 0; i < components.length; i++) {
|
||||
Slog.i(TAG, " * " + components[i]);
|
||||
}
|
||||
}
|
||||
|
||||
mTransportManager.onPackageChanged(pkgName, components);
|
||||
return; // nothing more to do in the PACKAGE_CHANGED case
|
||||
}
|
||||
|
||||
@@ -2015,19 +1923,7 @@ public class BackupManagerService {
|
||||
writeFullBackupScheduleAsync();
|
||||
}
|
||||
|
||||
// Transport maintenance: rebind to known existing transports that have
|
||||
// just been updated; and bind to any newly-installed transport services.
|
||||
synchronized (mTransports) {
|
||||
final TransportConnection conn = mTransportConnections.get(packageName);
|
||||
if (conn != null) {
|
||||
if (MORE_DEBUG) {
|
||||
Slog.i(TAG, "Transport package changed; rebinding");
|
||||
}
|
||||
bindTransport(conn.mTransport);
|
||||
} else {
|
||||
checkForTransportAndBind(app);
|
||||
}
|
||||
}
|
||||
mTransportManager.onPackageAdded(packageName);
|
||||
|
||||
} catch (NameNotFoundException e) {
|
||||
// doesn't really exist; ignore it
|
||||
@@ -2051,107 +1947,13 @@ public class BackupManagerService {
|
||||
removePackageParticipantsLocked(pkgList, uid);
|
||||
}
|
||||
}
|
||||
for (String pkgName : pkgList) {
|
||||
mTransportManager.onPackageRemoved(pkgName);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// ----- Track connection to transports service -----
|
||||
class TransportConnection implements ServiceConnection {
|
||||
ServiceInfo mTransport;
|
||||
|
||||
public TransportConnection(ServiceInfo transport) {
|
||||
mTransport = transport;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName component, IBinder service) {
|
||||
if (DEBUG) Slog.v(TAG, "Connected to transport " + component);
|
||||
final String name = component.flattenToShortString();
|
||||
try {
|
||||
IBackupTransport transport = IBackupTransport.Stub.asInterface(service);
|
||||
registerTransport(transport.name(), name, transport);
|
||||
EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, name, 1);
|
||||
} catch (Exception e) {
|
||||
Slog.e(TAG, "Unable to register transport " + component
|
||||
+ ": " + e.getMessage());
|
||||
EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, name, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName component) {
|
||||
if (DEBUG) Slog.v(TAG, "Disconnected from transport " + component);
|
||||
final String name = component.flattenToShortString();
|
||||
EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, name, 0);
|
||||
registerTransport(null, name, null);
|
||||
}
|
||||
};
|
||||
|
||||
// Check whether the given package hosts a transport, and bind if so
|
||||
void checkForTransportAndBind(PackageInfo pkgInfo) {
|
||||
Intent intent = new Intent(mTransportServiceIntent)
|
||||
.setPackage(pkgInfo.packageName);
|
||||
// TODO: http://b/22388012
|
||||
List<ResolveInfo> hosts = mPackageManager.queryIntentServicesAsUser(
|
||||
intent, 0, UserHandle.USER_SYSTEM);
|
||||
if (hosts != null) {
|
||||
final int N = hosts.size();
|
||||
for (int i = 0; i < N; i++) {
|
||||
final ServiceInfo info = hosts.get(i).serviceInfo;
|
||||
tryBindTransport(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Verify that the service exists and is hosted by a privileged app, then proceed to bind
|
||||
boolean tryBindTransport(ServiceInfo info) {
|
||||
try {
|
||||
PackageInfo packInfo = mPackageManager.getPackageInfo(info.packageName, 0);
|
||||
if ((packInfo.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_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;
|
||||
}
|
||||
|
||||
// Actually bind; presumes that we have already validated the transport service
|
||||
boolean bindTransport(ServiceInfo transport) {
|
||||
ComponentName svcName = new ComponentName(transport.packageName, transport.name);
|
||||
if (!mTransportWhitelist.contains(svcName)) {
|
||||
Slog.w(TAG, "Proposed transport " + svcName + " not whitelisted; ignoring");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (MORE_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);
|
||||
}
|
||||
}
|
||||
// TODO: http://b/22388012
|
||||
return mContext.bindServiceAsUser(intent,
|
||||
connection, Context.BIND_AUTO_CREATE,
|
||||
UserHandle.SYSTEM);
|
||||
}
|
||||
|
||||
// Add the backup agents in the given packages to our set of known backup participants.
|
||||
// If 'packageNames' is null, adds all backup agents in the whole system.
|
||||
void addPackageParticipantsLocked(String[] packageNames) {
|
||||
@@ -2352,34 +2154,12 @@ public class BackupManagerService {
|
||||
}
|
||||
}
|
||||
|
||||
// Return the given transport
|
||||
private IBackupTransport getTransport(String transportName) {
|
||||
synchronized (mTransports) {
|
||||
IBackupTransport transport = mTransports.get(transportName);
|
||||
if (transport == null) {
|
||||
Slog.w(TAG, "Requested unavailable transport: " + transportName);
|
||||
}
|
||||
return transport;
|
||||
}
|
||||
}
|
||||
|
||||
// What name is this transport registered under...?
|
||||
private String getTransportName(IBackupTransport transport) {
|
||||
if (MORE_DEBUG) {
|
||||
Slog.v(TAG, "Searching for transport name of " + transport);
|
||||
}
|
||||
synchronized (mTransports) {
|
||||
final int N = mTransports.size();
|
||||
for (int i = 0; i < N; i++) {
|
||||
if (mTransports.valueAt(i).equals(transport)) {
|
||||
if (MORE_DEBUG) {
|
||||
Slog.v(TAG, " Name found: " + mTransports.keyAt(i));
|
||||
}
|
||||
return mTransports.keyAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return mTransportManager.getTransportName(transport);
|
||||
}
|
||||
|
||||
// fire off a backup agent, blocking until it attaches or times out
|
||||
@@ -2505,7 +2285,7 @@ public class BackupManagerService {
|
||||
throw new IllegalArgumentException("No packages are provided for backup");
|
||||
}
|
||||
|
||||
IBackupTransport transport = getTransport(mCurrentTransport);
|
||||
IBackupTransport transport = mTransportManager.getCurrentTransportBinder();
|
||||
if (transport == null) {
|
||||
sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
|
||||
return BackupManager.ERROR_TRANSPORT_ABORTED;
|
||||
@@ -3025,7 +2805,7 @@ public class BackupManagerService {
|
||||
if (MORE_DEBUG) Slog.d(TAG, "Server requires init; rerunning");
|
||||
addBackupTrace("init required; rerunning");
|
||||
try {
|
||||
final String name = getTransportName(mTransport);
|
||||
final String name = mTransportManager.getTransportName(mTransport);
|
||||
if (name != null) {
|
||||
mPendingInits.add(name);
|
||||
} else {
|
||||
@@ -4503,7 +4283,7 @@ public class BackupManagerService {
|
||||
return;
|
||||
}
|
||||
|
||||
IBackupTransport transport = getTransport(mCurrentTransport);
|
||||
IBackupTransport transport = mTransportManager.getCurrentTransportBinder();
|
||||
if (transport == null) {
|
||||
Slog.w(TAG, "Transport not present; full data backup not performed");
|
||||
backupRunStatus = BackupManager.ERROR_TRANSPORT_ABORTED;
|
||||
@@ -5119,7 +4899,7 @@ public class BackupManagerService {
|
||||
|
||||
headBusy = false;
|
||||
|
||||
if (!fullBackupAllowable(getTransport(mCurrentTransport))) {
|
||||
if (!fullBackupAllowable(mTransportManager.getCurrentTransportBinder())) {
|
||||
if (MORE_DEBUG) {
|
||||
Slog.i(TAG, "Preconditions not met; not running full backup");
|
||||
}
|
||||
@@ -9115,7 +8895,8 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
|
||||
public void run() {
|
||||
try {
|
||||
for (String transportName : mQueue) {
|
||||
IBackupTransport transport = getTransport(transportName);
|
||||
IBackupTransport transport =
|
||||
mTransportManager.getTransportBinder(transportName);
|
||||
if (transport == null) {
|
||||
Slog.e(TAG, "Requested init for " + transportName + " but not found");
|
||||
continue;
|
||||
@@ -9312,7 +9093,8 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
|
||||
if (MORE_DEBUG) Slog.v(TAG, "Found the app - running clear process");
|
||||
mBackupHandler.removeMessages(MSG_RETRY_CLEAR);
|
||||
synchronized (mQueueLock) {
|
||||
final IBackupTransport transport = getTransport(transportName);
|
||||
final IBackupTransport transport =
|
||||
mTransportManager.getTransportBinder(transportName);
|
||||
if (transport == null) {
|
||||
// transport is currently unavailable -- make sure to retry
|
||||
Message msg = mBackupHandler.obtainMessage(MSG_RETRY_CLEAR,
|
||||
@@ -9450,7 +9232,7 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
|
||||
throw new IllegalStateException("Restore supported only for the device owner");
|
||||
}
|
||||
|
||||
if (!fullBackupAllowable(getTransport(mCurrentTransport))) {
|
||||
if (!fullBackupAllowable(mTransportManager.getCurrentTransportBinder())) {
|
||||
Slog.i(TAG, "Full backup not currently possible -- key/value backup not yet run?");
|
||||
} else {
|
||||
if (DEBUG) {
|
||||
@@ -9718,10 +9500,7 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
|
||||
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());
|
||||
}
|
||||
String[] allTransports = mTransportManager.getBoundTransportNames();
|
||||
// build the set of transports for which we are posting an init
|
||||
for (String transport : allTransports) {
|
||||
recordInitPendingLocked(true, transport);
|
||||
@@ -9774,36 +9553,27 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
|
||||
public String getCurrentTransport() {
|
||||
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
|
||||
"getCurrentTransport");
|
||||
if (MORE_DEBUG) Slog.v(TAG, "... getCurrentTransport() returning " + mCurrentTransport);
|
||||
return mCurrentTransport;
|
||||
String currentTransport = mTransportManager.getCurrentTransportName();
|
||||
if (MORE_DEBUG) Slog.v(TAG, "... getCurrentTransport() returning " + currentTransport);
|
||||
return currentTransport;
|
||||
}
|
||||
|
||||
// Report all known, available backup transports
|
||||
public 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());
|
||||
}
|
||||
}
|
||||
return mTransportManager.getBoundTransportNames();
|
||||
}
|
||||
|
||||
if (known.size() > 0) {
|
||||
list = new String[known.size()];
|
||||
known.toArray(list);
|
||||
}
|
||||
return list;
|
||||
public ComponentName[] listAllTransportComponents() {
|
||||
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
|
||||
"listAllTransportComponents");
|
||||
return mTransportManager.getAllTransportCompenents();
|
||||
}
|
||||
|
||||
public String[] getTransportWhitelist() {
|
||||
// No permission check, intentionally.
|
||||
String[] whitelist = new String[mTransportWhitelist.size()];
|
||||
for (int i = mTransportWhitelist.size() - 1; i >= 0; i--) {
|
||||
whitelist[i] = mTransportWhitelist.valueAt(i).flattenToShortString();
|
||||
}
|
||||
return whitelist;
|
||||
return mTransportManager.getTransportWhitelist().toArray(new String[0]);
|
||||
}
|
||||
|
||||
// Select which transport to use for the next backup operation.
|
||||
@@ -9811,22 +9581,58 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
|
||||
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);
|
||||
}
|
||||
final long oldId = Binder.clearCallingIdentity();
|
||||
try {
|
||||
String prevTransport = mTransportManager.selectTransport(transport);
|
||||
Settings.Secure.putString(mContext.getContentResolver(),
|
||||
Settings.Secure.BACKUP_TRANSPORT, transport);
|
||||
Slog.v(TAG, "selectBackupTransport() set " + mTransportManager.getCurrentTransportName()
|
||||
+ " returning " + prevTransport);
|
||||
return prevTransport;
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(oldId);
|
||||
}
|
||||
}
|
||||
|
||||
public void selectBackupTransportAsync(final ComponentName transport,
|
||||
final ISelectBackupTransportCallback listener) {
|
||||
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
|
||||
"selectBackupTransportAsync");
|
||||
|
||||
final long oldId = Binder.clearCallingIdentity();
|
||||
|
||||
Slog.v(TAG, "selectBackupTransportAsync() called with transport " +
|
||||
transport.flattenToShortString());
|
||||
|
||||
mTransportManager.ensureTransportReady(transport, new SelectBackupTransportCallback() {
|
||||
@Override
|
||||
public void onSuccess(String transportName) {
|
||||
mTransportManager.selectTransport(transportName);
|
||||
Settings.Secure.putString(mContext.getContentResolver(),
|
||||
Settings.Secure.BACKUP_TRANSPORT,
|
||||
mTransportManager.getCurrentTransportName());
|
||||
Slog.v(TAG, "Transport successfully selected: " + transport.flattenToShortString());
|
||||
try {
|
||||
listener.onSuccess(transportName);
|
||||
} catch (RemoteException e) {
|
||||
// Nothing to do here.
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(int reason) {
|
||||
Slog.v(TAG, "Failed to select transport: " + transport.flattenToShortString());
|
||||
try {
|
||||
listener.onFailure(reason);
|
||||
} catch (RemoteException e) {
|
||||
// Nothing to do here.
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Binder.restoreCallingIdentity(oldId);
|
||||
}
|
||||
|
||||
// Supply the configuration Intent for the given transport. If the name is not one
|
||||
// of the available transports, or if the transport does not supply any configuration
|
||||
// UI, the method returns null.
|
||||
@@ -9834,18 +9640,16 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
|
||||
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 (Exception e) {
|
||||
/* fall through to return null */
|
||||
Slog.e(TAG, "Unable to get configuration intent from transport: " + e.getMessage());
|
||||
}
|
||||
final IBackupTransport transport = mTransportManager.getTransportBinder(transportName);
|
||||
if (transport != null) {
|
||||
try {
|
||||
final Intent intent = transport.configurationIntent();
|
||||
if (MORE_DEBUG) Slog.d(TAG, "getConfigurationIntent() returning config intent "
|
||||
+ intent);
|
||||
return intent;
|
||||
} catch (Exception e) {
|
||||
/* fall through to return null */
|
||||
Slog.e(TAG, "Unable to get configuration intent from transport: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9861,17 +9665,15 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
|
||||
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 (Exception e) {
|
||||
/* fall through to return null */
|
||||
Slog.e(TAG, "Unable to get string from transport: " + e.getMessage());
|
||||
}
|
||||
final IBackupTransport transport = mTransportManager.getTransportBinder(transportName);
|
||||
if (transport != null) {
|
||||
try {
|
||||
final String text = transport.currentDestinationString();
|
||||
if (MORE_DEBUG) Slog.d(TAG, "getDestinationString() returning " + text);
|
||||
return text;
|
||||
} catch (Exception e) {
|
||||
/* fall through to return null */
|
||||
Slog.e(TAG, "Unable to get string from transport: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9883,18 +9685,16 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
|
||||
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 (Exception e) {
|
||||
/* fall through to return null */
|
||||
Slog.e(TAG, "Unable to get management intent from transport: " + e.getMessage());
|
||||
}
|
||||
final IBackupTransport transport = mTransportManager.getTransportBinder(transportName);
|
||||
if (transport != null) {
|
||||
try {
|
||||
final Intent intent = transport.dataManagementIntent();
|
||||
if (MORE_DEBUG) Slog.d(TAG, "getDataManagementIntent() returning intent "
|
||||
+ intent);
|
||||
return intent;
|
||||
} catch (Exception e) {
|
||||
/* fall through to return null */
|
||||
Slog.e(TAG, "Unable to get management intent from transport: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9907,17 +9707,15 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
|
||||
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 (Exception e) {
|
||||
/* fall through to return null */
|
||||
Slog.e(TAG, "Unable to get management label from transport: " + e.getMessage());
|
||||
}
|
||||
final IBackupTransport transport = mTransportManager.getTransportBinder(transportName);
|
||||
if (transport != null) {
|
||||
try {
|
||||
final String text = transport.dataManagementLabel();
|
||||
if (MORE_DEBUG) Slog.d(TAG, "getDataManagementLabel() returning " + text);
|
||||
return text;
|
||||
} catch (Exception e) {
|
||||
/* fall through to return null */
|
||||
Slog.e(TAG, "Unable to get management label from transport: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9979,7 +9777,7 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
|
||||
}
|
||||
|
||||
// Do we have a transport to fetch data for us?
|
||||
IBackupTransport transport = getTransport(mCurrentTransport);
|
||||
IBackupTransport transport = mTransportManager.getCurrentTransportBinder();
|
||||
if (transport == null) {
|
||||
if (DEBUG) Slog.w(TAG, "No transport");
|
||||
skip = true;
|
||||
@@ -10033,7 +9831,7 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
|
||||
|
||||
boolean needPermission = true;
|
||||
if (transport == null) {
|
||||
transport = mCurrentTransport;
|
||||
transport = mTransportManager.getCurrentTransportName();
|
||||
|
||||
if (packageName != null) {
|
||||
PackageInfo app = null;
|
||||
@@ -10127,7 +9925,7 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
|
||||
appIsStopped(packageInfo.applicationInfo)) {
|
||||
return false;
|
||||
}
|
||||
IBackupTransport transport = getTransport(mCurrentTransport);
|
||||
IBackupTransport transport = mTransportManager.getCurrentTransportBinder();
|
||||
if (transport != null) {
|
||||
try {
|
||||
return transport.isAppEligibleForBackup(packageInfo,
|
||||
@@ -10156,7 +9954,7 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
|
||||
|
||||
ActiveRestoreSession(String packageName, String transport) {
|
||||
mPackageName = packageName;
|
||||
mRestoreTransport = getTransport(transport);
|
||||
mRestoreTransport = mTransportManager.getTransportBinder(transport);
|
||||
}
|
||||
|
||||
public void markTimedOut() {
|
||||
@@ -10515,7 +10313,7 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
|
||||
pw.println(" next scheduled: " + KeyValueBackupJob.nextScheduled());
|
||||
|
||||
pw.println("Transport whitelist:");
|
||||
for (ComponentName transport : mTransportWhitelist) {
|
||||
for (ComponentName transport : mTransportManager.getTransportWhitelist()) {
|
||||
pw.print(" ");
|
||||
pw.println(transport.flattenToShortString());
|
||||
}
|
||||
@@ -10524,9 +10322,9 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
|
||||
final String[] transports = listAllTransports();
|
||||
if (transports != null) {
|
||||
for (String t : listAllTransports()) {
|
||||
pw.println((t.equals(mCurrentTransport) ? " * " : " ") + t);
|
||||
pw.println((t.equals(mTransportManager.getCurrentTransportName()) ? " * " : " ") + t);
|
||||
try {
|
||||
IBackupTransport transport = getTransport(t);
|
||||
IBackupTransport transport = mTransportManager.getTransportBinder(t);
|
||||
File dir = new File(mBaseStateDir, transport.transportDirName());
|
||||
pw.println(" destination: " + transport.currentDestinationString());
|
||||
pw.println(" intent: " + transport.configurationIntent());
|
||||
|
||||
@@ -20,6 +20,8 @@ import android.app.backup.IBackupManager;
|
||||
import android.app.backup.IBackupObserver;
|
||||
import android.app.backup.IFullBackupRestoreObserver;
|
||||
import android.app.backup.IRestoreSession;
|
||||
import android.app.backup.ISelectBackupTransportCallback;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Binder;
|
||||
@@ -274,6 +276,12 @@ public class Trampoline extends IBackupManager.Stub {
|
||||
return (svc != null) ? svc.listAllTransports() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComponentName[] listAllTransportComponents() throws RemoteException {
|
||||
BackupManagerService svc = mService;
|
||||
return (svc != null) ? svc.listAllTransportComponents() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getTransportWhitelist() {
|
||||
BackupManagerService svc = mService;
|
||||
@@ -286,6 +294,15 @@ public class Trampoline extends IBackupManager.Stub {
|
||||
return (svc != null) ? svc.selectBackupTransport(transport) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectBackupTransportAsync(ComponentName transport,
|
||||
ISelectBackupTransportCallback listener) throws RemoteException {
|
||||
BackupManagerService svc = mService;
|
||||
if (svc != null) {
|
||||
svc.selectBackupTransportAsync(transport, listener);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Intent getConfigurationIntent(String transport) throws RemoteException {
|
||||
BackupManagerService svc = mService;
|
||||
|
||||
@@ -0,0 +1,410 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.backup;
|
||||
|
||||
import android.app.backup.BackupManager;
|
||||
import android.app.backup.SelectBackupTransportCallback;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.EventLog;
|
||||
import android.util.Log;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.backup.IBackupTransport;
|
||||
import com.android.server.EventLogTags;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Handles in-memory bookkeeping of all BackupTransport objects.
|
||||
*/
|
||||
class TransportManager {
|
||||
|
||||
private static final String TAG = "BackupTransportManager";
|
||||
|
||||
private static final String SERVICE_ACTION_TRANSPORT_HOST = "android.backup.TRANSPORT_HOST";
|
||||
|
||||
private final Intent mTransportServiceIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST);
|
||||
private final Context mContext;
|
||||
private final PackageManager mPackageManager;
|
||||
private final Set<ComponentName> mTransportWhitelist;
|
||||
|
||||
/**
|
||||
* This listener is called after we bind to any transport. If it returns true, this is a valid
|
||||
* transport.
|
||||
*/
|
||||
private final TransportBoundListener mTransportBoundListener;
|
||||
|
||||
private String mCurrentTransportName;
|
||||
|
||||
/** Lock on this before accessing mValidTransports and mBoundTransports. */
|
||||
private final Object mTransportLock = new Object();
|
||||
|
||||
/**
|
||||
* We have detected these transports on the device. Unless in exceptional cases, we are also
|
||||
* bound to all of these.
|
||||
*/
|
||||
@GuardedBy("mTransportLock")
|
||||
private final Map<ComponentName, TransportConnection> mValidTransports = new ArrayMap<>();
|
||||
|
||||
/** We are currently bound to these transports. */
|
||||
@GuardedBy("mTransportLock")
|
||||
private final Map<String, ComponentName> mBoundTransports = new ArrayMap<>();
|
||||
|
||||
TransportManager(Context context, Set<ComponentName> whitelist, String defaultTransport,
|
||||
TransportBoundListener listener) {
|
||||
mContext = context;
|
||||
mPackageManager = context.getPackageManager();
|
||||
mTransportWhitelist = whitelist;
|
||||
mCurrentTransportName = defaultTransport;
|
||||
mTransportBoundListener = listener;
|
||||
}
|
||||
|
||||
void onPackageAdded(String packageName) {
|
||||
// New package added. Bind to all transports it contains.
|
||||
synchronized (mTransportLock) {
|
||||
log_verbose("Package added. Binding to all transports. " + packageName);
|
||||
bindToAllInternal(packageName, null /* all components */);
|
||||
}
|
||||
}
|
||||
|
||||
void onPackageRemoved(String packageName) {
|
||||
// Package removed. Remove all its transports from our list. These transports have already
|
||||
// been removed from mBoundTransports because onServiceDisconnected would already been
|
||||
// called on TransportConnection objects.
|
||||
synchronized (mTransportLock) {
|
||||
for (ComponentName transport : mValidTransports.keySet()) {
|
||||
if (transport.getPackageName().equals(packageName)) {
|
||||
TransportConnection removed = mValidTransports.remove(transport);
|
||||
if (removed != null) {
|
||||
mContext.unbindService(removed);
|
||||
log_verbose("Package removed, Removing transport: " +
|
||||
transport.flattenToShortString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void onPackageChanged(String packageName, String[] components) {
|
||||
synchronized (mTransportLock) {
|
||||
// Remove all changed components from mValidTransports. We'll bind to them again
|
||||
// and re-add them if still valid.
|
||||
for (String component : components) {
|
||||
ComponentName componentName = new ComponentName(packageName, component);
|
||||
TransportConnection removed = mValidTransports.remove(componentName);
|
||||
if (removed != null) {
|
||||
mContext.unbindService(removed);
|
||||
log_verbose("Package changed. Removing transport: " +
|
||||
componentName.flattenToShortString());
|
||||
}
|
||||
}
|
||||
bindToAllInternal(packageName, components);
|
||||
}
|
||||
}
|
||||
|
||||
IBackupTransport getTransportBinder(String transportName) {
|
||||
synchronized (mTransportLock) {
|
||||
ComponentName component = mBoundTransports.get(transportName);
|
||||
if (component == null) {
|
||||
Slog.w(TAG, "Transport " + transportName + " not bound.");
|
||||
return null;
|
||||
}
|
||||
TransportConnection conn = mValidTransports.get(component);
|
||||
if (conn == null) {
|
||||
Slog.w(TAG, "Transport " + transportName + " not valid.");
|
||||
return null;
|
||||
}
|
||||
return conn.getBinder();
|
||||
}
|
||||
}
|
||||
|
||||
IBackupTransport getCurrentTransportBinder() {
|
||||
return getTransportBinder(mCurrentTransportName);
|
||||
}
|
||||
|
||||
String getTransportName(IBackupTransport binder) {
|
||||
synchronized (mTransportLock) {
|
||||
for (TransportConnection conn : mValidTransports.values()) {
|
||||
if (conn.getBinder() == binder) {
|
||||
return conn.getName();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
String[] getBoundTransportNames() {
|
||||
synchronized (mTransportLock) {
|
||||
return mBoundTransports.keySet().toArray(new String[0]);
|
||||
}
|
||||
}
|
||||
|
||||
ComponentName[] getAllTransportCompenents() {
|
||||
synchronized (mTransportLock) {
|
||||
return mValidTransports.keySet().toArray(new ComponentName[0]);
|
||||
}
|
||||
}
|
||||
|
||||
String getCurrentTransportName() {
|
||||
return mCurrentTransportName;
|
||||
}
|
||||
|
||||
Set<ComponentName> getTransportWhitelist() {
|
||||
return mTransportWhitelist;
|
||||
}
|
||||
|
||||
String selectTransport(String transport) {
|
||||
synchronized (mTransportLock) {
|
||||
String prevTransport = mCurrentTransportName;
|
||||
mCurrentTransportName = transport;
|
||||
return prevTransport;
|
||||
}
|
||||
}
|
||||
|
||||
void ensureTransportReady(ComponentName transportComponent, SelectBackupTransportCallback listener) {
|
||||
synchronized (mTransportLock) {
|
||||
TransportConnection conn = mValidTransports.get(transportComponent);
|
||||
if (conn == null) {
|
||||
listener.onFailure(BackupManager.ERROR_TRANSPORT_UNAVAILABLE);
|
||||
return;
|
||||
}
|
||||
// Transport can be unbound if the process hosting it crashed.
|
||||
conn.bindIfUnbound();
|
||||
conn.addListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
void registerAllTransports() {
|
||||
bindToAllInternal(null /* all packages */, null /* all components */);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind to all transports belonging to the given package and the given component list.
|
||||
* null acts a wildcard.
|
||||
*
|
||||
* If packageName is null, bind to all transports in all packages.
|
||||
* If components is null, bind to all transports in the given package.
|
||||
*/
|
||||
private void bindToAllInternal(String packageName, String[] components) {
|
||||
PackageInfo pkgInfo = null;
|
||||
if (packageName != null) {
|
||||
try {
|
||||
pkgInfo = mPackageManager.getPackageInfo(packageName, 0);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Slog.w(TAG, "Package not found: " + packageName);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Intent intent = new Intent(mTransportServiceIntent);
|
||||
if (packageName != null) {
|
||||
intent.setPackage(packageName);
|
||||
}
|
||||
|
||||
List<ResolveInfo> hosts = mPackageManager.queryIntentServicesAsUser(
|
||||
intent, 0, UserHandle.USER_SYSTEM);
|
||||
if (hosts != null) {
|
||||
for (ResolveInfo host : hosts) {
|
||||
final ServiceInfo info = host.serviceInfo;
|
||||
boolean shouldBind = false;
|
||||
if (components != null && packageName != null) {
|
||||
for (String component : components) {
|
||||
ComponentName cn = new ComponentName(pkgInfo.packageName, component);
|
||||
if (info.getComponentName().equals(cn)) {
|
||||
shouldBind = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
shouldBind = true;
|
||||
}
|
||||
if (shouldBind && isTransportTrusted(info.getComponentName())) {
|
||||
tryBindTransport(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Transport has to be whitelisted and privileged. */
|
||||
private boolean isTransportTrusted(ComponentName transport) {
|
||||
if (!mTransportWhitelist.contains(transport)) {
|
||||
Slog.w(TAG, "BackupTransport " + transport.flattenToShortString() +
|
||||
" not whitelisted.");
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
PackageInfo packInfo = mPackageManager.getPackageInfo(transport.getPackageName(), 0);
|
||||
if ((packInfo.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED)
|
||||
== 0) {
|
||||
Slog.w(TAG, "Transport package " + transport.getPackageName() + " not privileged");
|
||||
return false;
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Slog.w(TAG, "Package not found.", e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void tryBindTransport(ServiceInfo transport) {
|
||||
Slog.d(TAG, "Binding to transport: " + transport.getComponentName().flattenToShortString());
|
||||
// TODO: b/22388012 (Multi user backup and restore)
|
||||
TransportConnection connection = new TransportConnection(transport.getComponentName());
|
||||
if (bindToTransport(transport.getComponentName(), connection)) {
|
||||
synchronized (mTransportLock) {
|
||||
mValidTransports.put(transport.getComponentName(), connection);
|
||||
}
|
||||
} else {
|
||||
Slog.w(TAG, "Couldn't bind to transport " + transport.getComponentName());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean bindToTransport(ComponentName componentName, ServiceConnection connection) {
|
||||
Intent intent = new Intent(mTransportServiceIntent)
|
||||
.setComponent(componentName);
|
||||
return mContext.bindServiceAsUser(intent, connection, Context.BIND_AUTO_CREATE,
|
||||
UserHandle.SYSTEM);
|
||||
}
|
||||
|
||||
private class TransportConnection implements ServiceConnection {
|
||||
|
||||
// Hold mTransportsLock to access these fields so as to provide a consistent view of them.
|
||||
private IBackupTransport mBinder;
|
||||
private final List<SelectBackupTransportCallback> mListeners = new ArrayList<>();
|
||||
private String mTransportName;
|
||||
|
||||
private final ComponentName mTransportComponent;
|
||||
|
||||
private TransportConnection(ComponentName transportComponent) {
|
||||
mTransportComponent = transportComponent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName component, IBinder binder) {
|
||||
synchronized (mTransportLock) {
|
||||
mBinder = IBackupTransport.Stub.asInterface(binder);
|
||||
boolean success = false;
|
||||
|
||||
EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE,
|
||||
component.flattenToShortString(), 1);
|
||||
|
||||
try {
|
||||
mTransportName = mBinder.name();
|
||||
// BackupManager requests some fields from the transport. If they are
|
||||
// invalid, throw away this transport.
|
||||
success = mTransportBoundListener.onTransportBound(mBinder);
|
||||
} catch (RemoteException e) {
|
||||
success = false;
|
||||
Slog.e(TAG, "Couldn't get transport name.", e);
|
||||
} finally {
|
||||
if (success) {
|
||||
Slog.d(TAG, "Bound to transport: " + component.flattenToShortString());
|
||||
mBoundTransports.put(mTransportName, component);
|
||||
for (SelectBackupTransportCallback listener : mListeners) {
|
||||
listener.onSuccess(mTransportName);
|
||||
}
|
||||
} else {
|
||||
Slog.w(TAG, "Bound to transport " + component.flattenToShortString() +
|
||||
" but it is invalid");
|
||||
EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE,
|
||||
component.flattenToShortString(), 0);
|
||||
mContext.unbindService(this);
|
||||
mValidTransports.remove(component);
|
||||
mBinder = null;
|
||||
for (SelectBackupTransportCallback listener : mListeners) {
|
||||
listener.onFailure(BackupManager.ERROR_TRANSPORT_INVALID);
|
||||
}
|
||||
}
|
||||
mListeners.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName component) {
|
||||
synchronized (mTransportLock) {
|
||||
mBinder = null;
|
||||
mBoundTransports.remove(mTransportName);
|
||||
}
|
||||
EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE,
|
||||
component.flattenToShortString(), 0);
|
||||
Slog.w(TAG, "Disconnected from transport " + component.flattenToShortString());
|
||||
}
|
||||
|
||||
private IBackupTransport getBinder() {
|
||||
synchronized (mTransportLock) {
|
||||
return mBinder;
|
||||
}
|
||||
}
|
||||
|
||||
private String getName() {
|
||||
synchronized (mTransportLock) {
|
||||
return mTransportName;
|
||||
}
|
||||
}
|
||||
|
||||
private void bindIfUnbound() {
|
||||
synchronized (mTransportLock) {
|
||||
if (mBinder == null) {
|
||||
Slog.d(TAG,
|
||||
"Rebinding to transport " + mTransportComponent.flattenToShortString());
|
||||
bindToTransport(mTransportComponent, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addListener(SelectBackupTransportCallback listener) {
|
||||
synchronized (mTransportLock) {
|
||||
if (mBinder == null) {
|
||||
// We are waiting for bind to complete. If mBinder is set to null after the bind
|
||||
// is complete due to transport being invalid, we won't find 'this' connection
|
||||
// object in mValidTransports list and this function can't be called.
|
||||
mListeners.add(listener);
|
||||
} else {
|
||||
listener.onSuccess(mTransportName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface TransportBoundListener {
|
||||
/** Should return true if this is a valid transport. */
|
||||
boolean onTransportBound(IBackupTransport binder);
|
||||
}
|
||||
|
||||
private static void log_verbose(String message) {
|
||||
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||
Slog.v(TAG, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user