From c46c4a6765196bcabf3ea89771a1f9067b22baad Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Thu, 14 Nov 2013 18:10:35 -0800 Subject: Handle backup transport registration dynamically Bug 11369873 Change-Id: I9bbdcc21ce25159c6645690123b5d03c553b0ddc --- .../android/internal/backup/IBackupTransport.aidl | 6 ++ .../android/internal/backup/LocalTransport.java | 5 + .../internal/backup/LocalTransportService.java | 37 +++++++ core/res/AndroidManifest.xml | 9 ++ .../com/android/server/BackupManagerService.java | 107 +++++++++++++-------- .../android/server/pm/PackageManagerService.java | 3 +- 6 files changed, 124 insertions(+), 43 deletions(-) create mode 100644 core/java/com/android/internal/backup/LocalTransportService.java diff --git a/core/java/com/android/internal/backup/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl index 5bfa1b236b3d..1e37fd9935a0 100644 --- a/core/java/com/android/internal/backup/IBackupTransport.aidl +++ b/core/java/com/android/internal/backup/IBackupTransport.aidl @@ -23,6 +23,12 @@ import android.os.ParcelFileDescriptor; /** {@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 eb2d1fe3e79a..494bc7898e93 100644 --- a/core/java/com/android/internal/backup/LocalTransport.java +++ b/core/java/com/android/internal/backup/LocalTransport.java @@ -19,6 +19,7 @@ package com.android.internal.backup; 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 class LocalTransport extends IBackupTransport.Stub { } } + 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 000000000000..d05699a73ae6 --- /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 bd52f492ab7e..6d74ee947251 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2681,6 +2681,15 @@ + + + + + + + diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java index a04ee144da29..baef607a4ec4 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.IPackageInstallObserver; 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 @@ class BackupManagerService extends IBackupManager.Stub { 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 @@ class BackupManagerService extends IBackupManager.Stub { volatile boolean mClearingData; // Transport bookkeeping + final HashMap mTransportNames + = new HashMap(); // component name -> registration name final HashMap mTransports - = new HashMap(); + = new HashMap(); // registration name -> binder + final ArrayList mTransportConnections + = new ArrayList(); String mCurrentTransport; - IBackupTransport mLocalTransport, mGoogleTransport; ActiveRestoreSession mActiveRestoreSession; // Watch the device provisioning operation during setup @@ -815,13 +821,7 @@ class BackupManagerService extends IBackupManager.Stub { } // 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 @@ class BackupManagerService extends IBackupManager.Stub { } 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 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); + } + } + 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()); + } } - } catch (PackageManager.NameNotFoundException nnf) { - // No such package? No binding. - if (DEBUG) Slog.v(TAG, "Google transport not present"); } // Now that we know about valid backup participants, parse any @@ -1298,13 +1313,16 @@ class BackupManagerService extends IBackupManager.Stub { // 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 @@ class BackupManagerService extends IBackupManager.Stub { } }; - // ----- 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 e075862a2cc2..e9a9f46c0cf9 100755 --- a/services/java/com/android/server/pm/PackageManagerService.java +++ b/services/java/com/android/server/pm/PackageManagerService.java @@ -1269,7 +1269,8 @@ public class PackageManagerService extends IPackageManager.Stub { 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. -- cgit v1.2.3