Handle backup transport registration dynamically
Bug 11369873
Change-Id: I9bbdcc21ce25159c6645690123b5d03c553b0ddc
diff --git a/core/java/com/android/internal/backup/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl
index 5bfa1b2..1e37fd9 100644
--- a/core/java/com/android/internal/backup/IBackupTransport.aidl
+++ b/core/java/com/android/internal/backup/IBackupTransport.aidl
@@ -23,6 +23,12 @@
/** {@hide} */
interface IBackupTransport {
+ /**
+ * Ask the transport for the name under which it should be registered. This will
+ * typically be its host service's component name, but need not be.
+ */
+ String name();
+
/**
* Ask the transport for an Intent that can be used to launch any internal
* configuration Activity that it wishes to present. For example, the transport
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index eb2d1fe..494bc78 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -19,6 +19,7 @@
import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataOutput;
import android.app.backup.RestoreSet;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
@@ -71,6 +72,10 @@
}
}
+ public String name() {
+ return new ComponentName(mContext, this.getClass()).flattenToShortString();
+ }
+
public Intent configurationIntent() {
// The local transport is not user-configurable
return null;
diff --git a/core/java/com/android/internal/backup/LocalTransportService.java b/core/java/com/android/internal/backup/LocalTransportService.java
new file mode 100644
index 0000000..d05699a
--- /dev/null
+++ b/core/java/com/android/internal/backup/LocalTransportService.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2013 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.internal.backup;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+public class LocalTransportService extends Service {
+ private static LocalTransport sTransport = null;
+
+ @Override
+ public void onCreate() {
+ if (sTransport == null) {
+ sTransport = new LocalTransport(this);
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return sTransport;
+ }
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 2c5c6b5..b198937 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2689,6 +2689,15 @@
<service android:name="android.hardware.location.GeofenceHardwareService"
android:permission="android.permission.LOCATION_HARDWARE"
android:exported="false" />
+
+ <service android:name="com.android.internal.backup.LocalTransportService"
+ android:permission="android.permission.CONFIRM_FULL_BACKUP"
+ android:exported="false">
+ <intent-filter>
+ <action android:name="android.backup.TRANSPORT_HOST" />
+ </intent-filter>
+ </service>
+
</application>
</manifest>
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index a04ee14..baef607 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -46,6 +46,8 @@
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.database.ContentObserver;
@@ -146,6 +148,7 @@
static final boolean COMPRESS_FULL_BACKUPS = true; // should be true in production
static final String SHARED_BACKUP_AGENT_PACKAGE = "com.android.sharedstoragebackup";
+ static final String SERVICE_ACTION_TRANSPORT_HOST = "android.backup.TRANSPORT_HOST";
// How often we perform a backup pass. Privileged external callers can
// trigger an immediate pass.
@@ -251,10 +254,13 @@
volatile boolean mClearingData;
// Transport bookkeeping
+ final HashMap<String,String> mTransportNames
+ = new HashMap<String,String>(); // component name -> registration name
final HashMap<String,IBackupTransport> mTransports
- = new HashMap<String,IBackupTransport>();
+ = new HashMap<String,IBackupTransport>(); // registration name -> binder
+ final ArrayList<TransportConnection> mTransportConnections
+ = new ArrayList<TransportConnection>();
String mCurrentTransport;
- IBackupTransport mLocalTransport, mGoogleTransport;
ActiveRestoreSession mActiveRestoreSession;
// Watch the device provisioning operation during setup
@@ -815,13 +821,7 @@
}
// Set up our transport options and initialize the default transport
- // TODO: Have transports register themselves somehow?
// TODO: Don't create transports that we don't need to?
- mLocalTransport = new LocalTransport(context); // This is actually pretty cheap
- ComponentName localName = new ComponentName(context, LocalTransport.class);
- registerTransport(localName.flattenToShortString(), mLocalTransport);
-
- mGoogleTransport = null;
mCurrentTransport = Settings.Secure.getString(context.getContentResolver(),
Settings.Secure.BACKUP_TRANSPORT);
if ("".equals(mCurrentTransport)) {
@@ -829,28 +829,43 @@
}
if (DEBUG) Slog.v(TAG, "Starting with transport " + mCurrentTransport);
- // Attach to the Google backup transport. When this comes up, it will set
- // itself as the current transport because we explicitly reset mCurrentTransport
- // to null.
- ComponentName transportComponent = new ComponentName("com.google.android.backup",
- "com.google.android.backup.BackupTransportService");
- try {
- // If there's something out there that is supposed to be the Google
- // backup transport, make sure it's legitimately part of the OS build
- // and not an app lying about its package name.
- ApplicationInfo info = mPackageManager.getApplicationInfo(
- transportComponent.getPackageName(), 0);
- if ((info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
- if (DEBUG) Slog.v(TAG, "Binding to Google transport");
- Intent intent = new Intent().setComponent(transportComponent);
- context.bindServiceAsUser(intent, mGoogleConnection, Context.BIND_AUTO_CREATE,
- UserHandle.OWNER);
- } else {
- Slog.w(TAG, "Possible Google transport spoof: ignoring " + info);
+ // Find transport hosts and bind to their services
+ Intent transportServiceIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST);
+ List<ResolveInfo> hosts = mPackageManager.queryIntentServicesAsUser(
+ transportServiceIntent, 0, UserHandle.USER_OWNER);
+ if (DEBUG) {
+ Slog.v(TAG, "Found transports: " + ((hosts == null) ? "null" : hosts.size()));
+ }
+ if (hosts != null) {
+ if (MORE_DEBUG) {
+ for (int i = 0; i < hosts.size(); i++) {
+ ServiceInfo info = hosts.get(i).serviceInfo;
+ Slog.v(TAG, " " + info.packageName + "/" + info.name);
+ }
}
- } catch (PackageManager.NameNotFoundException nnf) {
- // No such package? No binding.
- if (DEBUG) Slog.v(TAG, "Google transport not present");
+ for (int i = 0; i < hosts.size(); i++) {
+ try {
+ ServiceInfo info = hosts.get(i).serviceInfo;
+ PackageInfo packInfo = mPackageManager.getPackageInfo(info.packageName, 0);
+ if ((packInfo.applicationInfo.flags & ApplicationInfo.FLAG_PRIVILEGED) != 0) {
+ ComponentName svcName = new ComponentName(info.packageName, info.name);
+ if (DEBUG) {
+ Slog.i(TAG, "Binding to transport host " + svcName);
+ }
+ Intent intent = new Intent(transportServiceIntent);
+ intent.setComponent(svcName);
+ TransportConnection connection = new TransportConnection();
+ mTransportConnections.add(connection);
+ context.bindServiceAsUser(intent,
+ connection, Context.BIND_AUTO_CREATE,
+ UserHandle.OWNER);
+ } else {
+ Slog.w(TAG, "Transport package not privileged: " + info.packageName);
+ }
+ } catch (Exception e) {
+ Slog.e(TAG, "Problem resolving transport service: " + e.getMessage());
+ }
+ }
}
// Now that we know about valid backup participants, parse any
@@ -1298,13 +1313,16 @@
// 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, IBackupTransport transport) {
+ private void registerTransport(String name, String component, IBackupTransport transport) {
synchronized (mTransports) {
- if (DEBUG) Slog.v(TAG, "Registering transport " + name + " = " + transport);
+ if (DEBUG) Slog.v(TAG, "Registering transport "
+ + component + "::" + name + " = " + transport);
if (transport != null) {
mTransports.put(name, transport);
+ mTransportNames.put(component, name);
} else {
- mTransports.remove(name);
+ mTransports.remove(mTransportNames.get(component));
+ mTransportNames.remove(component);
// Nothing further to do in the unregistration case
return;
}
@@ -1390,18 +1408,23 @@
}
};
- // ----- Track connection to GoogleBackupTransport service -----
- ServiceConnection mGoogleConnection = new ServiceConnection() {
- public void onServiceConnected(ComponentName name, IBinder service) {
- if (DEBUG) Slog.v(TAG, "Connected to Google transport");
- mGoogleTransport = IBackupTransport.Stub.asInterface(service);
- registerTransport(name.flattenToShortString(), mGoogleTransport);
+ // ----- Track connection to transports service -----
+ class TransportConnection implements ServiceConnection {
+ @Override
+ public void onServiceConnected(ComponentName component, IBinder service) {
+ if (DEBUG) Slog.v(TAG, "Connected to transport " + component);
+ try {
+ IBackupTransport transport = IBackupTransport.Stub.asInterface(service);
+ registerTransport(transport.name(), component.flattenToShortString(), transport);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to register transport " + component);
+ }
}
- public void onServiceDisconnected(ComponentName name) {
- if (DEBUG) Slog.v(TAG, "Disconnected from Google transport");
- mGoogleTransport = null;
- registerTransport(name.flattenToShortString(), null);
+ @Override
+ public void onServiceDisconnected(ComponentName component) {
+ if (DEBUG) Slog.v(TAG, "Disconnected from transport " + component);
+ registerTransport(null, component.flattenToShortString(), null);
}
};
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 7ae9251..0623017 100755
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -1278,7 +1278,8 @@
frameworkDir.getPath(), OBSERVER_EVENTS, true, false);
mFrameworkInstallObserver.startWatching();
scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM
- | PackageParser.PARSE_IS_SYSTEM_DIR,
+ | PackageParser.PARSE_IS_SYSTEM_DIR
+ | PackageParser.PARSE_IS_PRIVILEGED,
scanMode | SCAN_NO_DEX, 0);
// Collected privileged system packages.