Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cefba58d authored by Christopher Tate's avatar Christopher Tate
Browse files

Handle backup transport registration dynamically

Bug 11369873

Change-Id: I9bbdcc21ce25159c6645690123b5d03c553b0ddc
parent a951fa56
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -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
+5 −0
Original line number Diff line number Diff line
@@ -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;
+37 −0
Original line number Diff line number Diff line
/*
 * 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;
    }
}
+9 −0
Original line number Diff line number Diff line
@@ -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>
+65 −42
Original line number Diff line number Diff line
@@ -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<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 @@ 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");
        // 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);
                }
            }
            for (int i = 0; i < hosts.size(); i++) {
                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,
                    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, "Possible Google transport spoof: ignoring " + info);
                        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);
        }
    };

Loading