Merge "Handle backup transport registration dynamically" into klp-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
22010817b9
@@ -23,6 +23,12 @@ import android.os.ParcelFileDescriptor;
|
|||||||
|
|
||||||
/** {@hide} */
|
/** {@hide} */
|
||||||
interface IBackupTransport {
|
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
|
* 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
|
* configuration Activity that it wishes to present. For example, the transport
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package com.android.internal.backup;
|
|||||||
import android.app.backup.BackupDataInput;
|
import android.app.backup.BackupDataInput;
|
||||||
import android.app.backup.BackupDataOutput;
|
import android.app.backup.BackupDataOutput;
|
||||||
import android.app.backup.RestoreSet;
|
import android.app.backup.RestoreSet;
|
||||||
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageInfo;
|
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() {
|
public Intent configurationIntent() {
|
||||||
// The local transport is not user-configurable
|
// The local transport is not user-configurable
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2689,6 +2689,15 @@
|
|||||||
<service android:name="android.hardware.location.GeofenceHardwareService"
|
<service android:name="android.hardware.location.GeofenceHardwareService"
|
||||||
android:permission="android.permission.LOCATION_HARDWARE"
|
android:permission="android.permission.LOCATION_HARDWARE"
|
||||||
android:exported="false" />
|
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>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|||||||
@@ -46,6 +46,8 @@ import android.content.pm.IPackageInstallObserver;
|
|||||||
import android.content.pm.IPackageManager;
|
import android.content.pm.IPackageManager;
|
||||||
import android.content.pm.PackageInfo;
|
import android.content.pm.PackageInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.pm.ResolveInfo;
|
||||||
|
import android.content.pm.ServiceInfo;
|
||||||
import android.content.pm.Signature;
|
import android.content.pm.Signature;
|
||||||
import android.content.pm.PackageManager.NameNotFoundException;
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
import android.database.ContentObserver;
|
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 boolean COMPRESS_FULL_BACKUPS = true; // should be true in production
|
||||||
|
|
||||||
static final String SHARED_BACKUP_AGENT_PACKAGE = "com.android.sharedstoragebackup";
|
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
|
// How often we perform a backup pass. Privileged external callers can
|
||||||
// trigger an immediate pass.
|
// trigger an immediate pass.
|
||||||
@@ -251,10 +254,13 @@ class BackupManagerService extends IBackupManager.Stub {
|
|||||||
volatile boolean mClearingData;
|
volatile boolean mClearingData;
|
||||||
|
|
||||||
// Transport bookkeeping
|
// Transport bookkeeping
|
||||||
|
final HashMap<String,String> mTransportNames
|
||||||
|
= new HashMap<String,String>(); // component name -> registration name
|
||||||
final HashMap<String,IBackupTransport> mTransports
|
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;
|
String mCurrentTransport;
|
||||||
IBackupTransport mLocalTransport, mGoogleTransport;
|
|
||||||
ActiveRestoreSession mActiveRestoreSession;
|
ActiveRestoreSession mActiveRestoreSession;
|
||||||
|
|
||||||
// Watch the device provisioning operation during setup
|
// 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
|
// 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?
|
// 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(),
|
mCurrentTransport = Settings.Secure.getString(context.getContentResolver(),
|
||||||
Settings.Secure.BACKUP_TRANSPORT);
|
Settings.Secure.BACKUP_TRANSPORT);
|
||||||
if ("".equals(mCurrentTransport)) {
|
if ("".equals(mCurrentTransport)) {
|
||||||
@@ -829,28 +829,43 @@ class BackupManagerService extends IBackupManager.Stub {
|
|||||||
}
|
}
|
||||||
if (DEBUG) Slog.v(TAG, "Starting with transport " + mCurrentTransport);
|
if (DEBUG) Slog.v(TAG, "Starting with transport " + mCurrentTransport);
|
||||||
|
|
||||||
// Attach to the Google backup transport. When this comes up, it will set
|
// Find transport hosts and bind to their services
|
||||||
// itself as the current transport because we explicitly reset mCurrentTransport
|
Intent transportServiceIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST);
|
||||||
// to null.
|
List<ResolveInfo> hosts = mPackageManager.queryIntentServicesAsUser(
|
||||||
ComponentName transportComponent = new ComponentName("com.google.android.backup",
|
transportServiceIntent, 0, UserHandle.USER_OWNER);
|
||||||
"com.google.android.backup.BackupTransportService");
|
if (DEBUG) {
|
||||||
try {
|
Slog.v(TAG, "Found transports: " + ((hosts == null) ? "null" : hosts.size()));
|
||||||
// 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
|
if (hosts != null) {
|
||||||
// and not an app lying about its package name.
|
if (MORE_DEBUG) {
|
||||||
ApplicationInfo info = mPackageManager.getApplicationInfo(
|
for (int i = 0; i < hosts.size(); i++) {
|
||||||
transportComponent.getPackageName(), 0);
|
ServiceInfo info = hosts.get(i).serviceInfo;
|
||||||
if ((info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
|
Slog.v(TAG, " " + info.packageName + "/" + info.name);
|
||||||
if (DEBUG) Slog.v(TAG, "Binding to Google transport");
|
}
|
||||||
Intent intent = new Intent().setComponent(transportComponent);
|
}
|
||||||
context.bindServiceAsUser(intent, mGoogleConnection, Context.BIND_AUTO_CREATE,
|
for (int i = 0; i < hosts.size(); i++) {
|
||||||
UserHandle.OWNER);
|
try {
|
||||||
} else {
|
ServiceInfo info = hosts.get(i).serviceInfo;
|
||||||
Slog.w(TAG, "Possible Google transport spoof: ignoring " + info);
|
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
|
// 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
|
// 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.
|
// 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) {
|
synchronized (mTransports) {
|
||||||
if (DEBUG) Slog.v(TAG, "Registering transport " + name + " = " + transport);
|
if (DEBUG) Slog.v(TAG, "Registering transport "
|
||||||
|
+ component + "::" + name + " = " + transport);
|
||||||
if (transport != null) {
|
if (transport != null) {
|
||||||
mTransports.put(name, transport);
|
mTransports.put(name, transport);
|
||||||
|
mTransportNames.put(component, name);
|
||||||
} else {
|
} else {
|
||||||
mTransports.remove(name);
|
mTransports.remove(mTransportNames.get(component));
|
||||||
|
mTransportNames.remove(component);
|
||||||
// Nothing further to do in the unregistration case
|
// Nothing further to do in the unregistration case
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1390,18 +1408,23 @@ class BackupManagerService extends IBackupManager.Stub {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// ----- Track connection to GoogleBackupTransport service -----
|
// ----- Track connection to transports service -----
|
||||||
ServiceConnection mGoogleConnection = new ServiceConnection() {
|
class TransportConnection implements ServiceConnection {
|
||||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
@Override
|
||||||
if (DEBUG) Slog.v(TAG, "Connected to Google transport");
|
public void onServiceConnected(ComponentName component, IBinder service) {
|
||||||
mGoogleTransport = IBackupTransport.Stub.asInterface(service);
|
if (DEBUG) Slog.v(TAG, "Connected to transport " + component);
|
||||||
registerTransport(name.flattenToShortString(), mGoogleTransport);
|
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) {
|
@Override
|
||||||
if (DEBUG) Slog.v(TAG, "Disconnected from Google transport");
|
public void onServiceDisconnected(ComponentName component) {
|
||||||
mGoogleTransport = null;
|
if (DEBUG) Slog.v(TAG, "Disconnected from transport " + component);
|
||||||
registerTransport(name.flattenToShortString(), null);
|
registerTransport(null, component.flattenToShortString(), null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1278,7 +1278,8 @@ public class PackageManagerService extends IPackageManager.Stub {
|
|||||||
frameworkDir.getPath(), OBSERVER_EVENTS, true, false);
|
frameworkDir.getPath(), OBSERVER_EVENTS, true, false);
|
||||||
mFrameworkInstallObserver.startWatching();
|
mFrameworkInstallObserver.startWatching();
|
||||||
scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM
|
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);
|
scanMode | SCAN_NO_DEX, 0);
|
||||||
|
|
||||||
// Collected privileged system packages.
|
// Collected privileged system packages.
|
||||||
|
|||||||
Reference in New Issue
Block a user