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

Commit 7f14edea authored by Bernardo Rufino's avatar Bernardo Rufino
Browse files

Binding on-demand #6: Transport attributes usage

Migrate the attribute queries from the Transport to the
TransportManager. Migrate all calls except currentDestinationString
because that's the one that changes and we should only migrate
after we have GMSCore that implements the push-from-transport
model.

Looking at method recordInitPendingLocked(), we only sent
MSG_RETRY_INIT if the transport threw while calling transportDirName
or the binder was null. With binding on-demand both of these cases
can't happen - i.e. we can't fail anymore. So, I removed the
message entirely.

Change-Id: I45a305704274c8b0c88637e3ccafc658639b2dfa
Ref: http://go/br-binding-on-demand
Bug: 17140907
Test: m -j RunFrameworksServicesRoboTests
Test: gts-tradefed run commandAndExit gts-dev -m GtsBackupTestCases
Test: gts-tradefed run commandAndExit gts-dev -m GtsBackupHostTestCases
Test: cts-tradefed run commandAndExit cts-dev -m CtsBackupTestCases
Test: runtest -p com.android.server.backup frameworks-services
Test: adb shell bmgr backupnow <packages>
Test: adb shell bmgr fullbackup <packages>
Test: adb shell cmd jobscheduler run -f android <job_id>
Test: adb shell bmgr enable false (being enabled before)
Test: adb shell dumpsys backup
Test: adb shell bmgr init <transport>
Test: Observed logs and used debugger to check proper code was being
Test: called in above commands
parent c4cded9c
Loading
Loading
Loading
Loading
+86 −124
Original line number Diff line number Diff line
@@ -25,7 +25,6 @@ import static com.android.server.backup.internal.BackupHandler.MSG_REQUEST_BACKU
import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_SESSION_TIMEOUT;
import static com.android.server.backup.internal.BackupHandler.MSG_RETRY_CLEAR;
import static com.android.server.backup.internal.BackupHandler.MSG_RETRY_INIT;
import static com.android.server.backup.internal.BackupHandler.MSG_RUN_ADB_BACKUP;
import static com.android.server.backup.internal.BackupHandler.MSG_RUN_ADB_RESTORE;
import static com.android.server.backup.internal.BackupHandler.MSG_RUN_CLEAR;
@@ -120,6 +119,7 @@ import com.android.server.backup.params.RestoreParams;
import com.android.server.backup.restore.ActiveRestoreSession;
import com.android.server.backup.restore.PerformUnifiedRestoreTask;
import com.android.server.backup.transport.TransportClient;
import com.android.server.backup.transport.TransportNotRegisteredException;
import com.android.server.backup.utils.AppBackupUtils;
import com.android.server.backup.utils.BackupManagerMonitorUtils;
import com.android.server.backup.utils.BackupObserverUtils;
@@ -1083,19 +1083,18 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
        return mBackupPasswordManager.backupPasswordMatches(currentPw);
    }

    // Maintain persistent state around whether need to do an initialize operation.
    // Must be called with the queue lock held.
    public void recordInitPendingLocked(boolean isPending, String transportName) {
    /**
     * Maintain persistent state around whether need to do an initialize operation.
     * Must be called with the queue lock held.
     */
    @GuardedBy("mQueueLock")
    public void recordInitPendingLocked(
            boolean isPending, String transportName, String transportDirName) {
        if (MORE_DEBUG) {
            Slog.i(TAG, "recordInitPendingLocked: " + isPending
                    + " on transport " + transportName);
        }
        mBackupHandler.removeMessages(MSG_RETRY_INIT);

        try {
            IBackupTransport transport = mTransportManager.getTransportBinder(transportName);
            if (transport != null) {
                String transportDirName = transport.transportDirName();
        File stateDir = new File(mBaseStateDir, transportDirName);
        File initPendingFile = new File(stateDir, INIT_SENTINEL_FILE_NAME);

@@ -1114,26 +1113,6 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
            initPendingFile.delete();
            mPendingInits.remove(transportName);
        }
                return; // done; don't fall through to the error case
            }
        } catch (Exception e) {
            // transport threw when asked its name; fall through to the lookup-failed case
            Slog.e(TAG, "Transport " + transportName + " failed to report name: "
                    + e.getMessage());
        }

        // The named transport doesn't exist or threw.  This operation is
        // important, so we record the need for a an init and post a message
        // to retry the init later.
        if (isPending) {
            mPendingInits.add(transportName);
            mBackupHandler.sendMessageDelayed(
                    mBackupHandler.obtainMessage(MSG_RETRY_INIT,
                            (isPending ? 1 : 0),
                            0,
                            transportName),
                    TRANSPORT_RETRY_INTERVAL);
        }
    }

    // Reset all of our bookkeeping, in response to having been told that
@@ -1614,27 +1593,9 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
            return BackupManager.ERROR_BACKUP_NOT_ALLOWED;
        }

        // We're using pieces of the new binding on-demand infra-structure and the old always-bound
        // infra-structure below this comment. The TransportManager.getCurrentTransportClient() line
        // is using the new one and TransportManager.getCurrentTransportBinder() is using the old.
        // This is weird but there is a reason.
        // This is the natural place to put TransportManager.getCurrentTransportClient() because of
        // the null handling below that should be the same for TransportClient.
        // TransportClient.connect() would return a IBackupTransport for us (instead of using the
        // old infra), but it may block and we don't want this in this thread.
        // The only usage of transport in this method is for transport.transportDirName(). When the
        // push-from-transport part of binding on-demand is in place we will replace the calls for
        // IBackupTransport.transportDirName() with calls for
        // TransportManager.transportDirName(transportName) or similar. So we'll leave the old piece
        // here until we implement that.
        // TODO(brufino): Remove always-bound code mTransportManager.getCurrentTransportBinder()
        TransportClient transportClient =
                mTransportManager.getCurrentTransportClient("BMS.requestBackup()");
        IBackupTransport transport = mTransportManager.getCurrentTransportBinder();
        if (transportClient == null || transport == null) {
            if (transportClient != null) {
                mTransportManager.disposeOfTransportClient(transportClient, "BMS.requestBackup()");
            }
        if (transportClient == null) {
            BackupObserverUtils.sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
            monitor = BackupManagerMonitorUtils.monitorEvent(monitor,
                    BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_IS_NULL,
@@ -1679,15 +1640,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
                    + " k/v backups");
        }

        String dirName;
        try {
            dirName = transport.transportDirName();
        } catch (Exception e) {
            Slog.e(TAG, "Transport unavailable while attempting backup: " + e.getMessage());
            BackupObserverUtils.sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
            return BackupManager.ERROR_TRANSPORT_ABORTED;
        }

        String dirName = transportClient.getTransportDirName();
        boolean nonIncrementalBackup = (flags & BackupManager.FLAG_NON_INCREMENTAL_BACKUP) != 0;

        Message msg = mBackupHandler.obtainMessage(MSG_REQUEST_BACKUP);
@@ -1998,16 +1951,17 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
        writeFullBackupScheduleAsync();
    }

    private boolean fullBackupAllowable(IBackupTransport transport) {
        if (transport == null) {
            Slog.w(TAG, "Transport not present; full data backup not performed");
    private boolean fullBackupAllowable(String transportName) {
        if (!mTransportManager.isTransportRegistered(transportName)) {
            Slog.w(TAG, "Transport not registered; full data backup not performed");
            return false;
        }

        // Don't proceed unless we have already established package metadata
        // for the current dataset via a key/value backup pass.
        try {
            File stateDir = new File(mBaseStateDir, transport.transportDirName());
            String transportDirName = mTransportManager.getTransportDirName(transportName);
            File stateDir = new File(mBaseStateDir, transportDirName);
            File pmState = new File(stateDir, PACKAGE_MANAGER_SENTINEL);
            if (pmState.length() <= 0) {
                if (DEBUG) {
@@ -2097,7 +2051,8 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter

                headBusy = false;

                if (!fullBackupAllowable(mTransportManager.getCurrentTransportBinder())) {
                String transportName = mTransportManager.getCurrentTransportName();
                if (!fullBackupAllowable(transportName)) {
                    if (MORE_DEBUG) {
                        Slog.i(TAG, "Preconditions not met; not running full backup");
                    }
@@ -2545,7 +2500,8 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
            throw new IllegalStateException("Restore supported only for the device owner");
        }

        if (!fullBackupAllowable(mTransportManager.getCurrentTransportBinder())) {
        String transportName = mTransportManager.getCurrentTransportName();
        if (!fullBackupAllowable(transportName)) {
            Slog.i(TAG, "Full backup not currently possible -- key/value backup not yet run?");
        } else {
            if (DEBUG) {
@@ -2826,10 +2782,30 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
                    if (wasEnabled && mProvisioned) {
                        // NOTE: we currently flush every registered transport, not just
                        // the currently-active one.
                        String[] allTransports = mTransportManager.getBoundTransportNames();
                        List<String> transportNames = new ArrayList<>();
                        List<String> transportDirNames = new ArrayList<>();
                        mTransportManager.forEachRegisteredTransport(
                                name -> {
                                    final String dirName;
                                    try {
                                        dirName =
                                                mTransportManager
                                                        .getTransportDirName(name);
                                    } catch (TransportNotRegisteredException e) {
                                        // Should never happen
                                        Slog.e(TAG, "Unexpected unregistered transport", e);
                                        return;
                                    }
                                    transportNames.add(name);
                                    transportDirNames.add(dirName);
                                });

                        // build the set of transports for which we are posting an init
                        for (String transport : allTransports) {
                            recordInitPendingLocked(true, transport);
                        for (int i = 0; i < transportNames.size(); i++) {
                            recordInitPendingLocked(
                                    true,
                                    transportNames.get(i),
                                    transportDirNames.get(i));
                        }
                        mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
                                mRunInitIntent);
@@ -2993,7 +2969,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter

        final long oldId = Binder.clearCallingIdentity();
        try {
            mTransportManager.describeTransport(
            mTransportManager.updateTransportAttributes(
                    transportComponent,
                    name,
                    configurationIntent,
@@ -3093,24 +3069,17 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
    public Intent getConfigurationIntent(String transportName) {
        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
                "getConfigurationIntent");

        final IBackupTransport transport = mTransportManager.getTransportBinder(transportName);
        if (transport != null) {
        try {
                final Intent intent = transport.configurationIntent();
            Intent intent = mTransportManager.getTransportConfigurationIntent(transportName);
            if (MORE_DEBUG) {
                    Slog.d(TAG, "getConfigurationIntent() returning config intent "
                            + intent);
                Slog.d(TAG, "getConfigurationIntent() returning intent " + intent);
            }
            return intent;
            } catch (Exception e) {
                /* fall through to return null */
        } catch (TransportNotRegisteredException e) {
            Slog.e(TAG, "Unable to get configuration intent from transport: " + e.getMessage());
            }
        }

            return null;
        }
    }

    // Supply the configuration summary string for the given transport.  If the name is
    // not one of the available transports, or if the transport does not supply any
@@ -3143,23 +3112,17 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
                "getDataManagementIntent");

        final IBackupTransport transport = mTransportManager.getTransportBinder(transportName);
        if (transport != null) {
        try {
                final Intent intent = transport.dataManagementIntent();
            Intent intent = mTransportManager.getTransportDataManagementIntent(transportName);
            if (MORE_DEBUG) {
                    Slog.d(TAG, "getDataManagementIntent() returning intent "
                            + intent);
                Slog.d(TAG, "getDataManagementIntent() returning intent " + intent);
            }
            return intent;
            } catch (Exception e) {
                /* fall through to return null */
        } catch (TransportNotRegisteredException e) {
            Slog.e(TAG, "Unable to get management intent from transport: " + e.getMessage());
            }
        }

            return null;
        }
    }

    // Supply the menu label for affordances that fire the manage-data intent
    // for the given transport.
@@ -3168,20 +3131,17 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
                "getDataManagementLabel");

        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());
            }
            String label = mTransportManager.getTransportDataManagementLabel(transportName);
            if (MORE_DEBUG) {
                Slog.d(TAG, "getDataManagementLabel() returning " + label);
            }

            return label;
        } catch (TransportNotRegisteredException e) {
            Slog.e(TAG, "Unable to get management label from transport: " + e.getMessage());
            return null;
        }
    }

    // Callback: a requested backup agent has been instantiated.  This should only
    // be called from the Activity Manager.
@@ -3497,14 +3457,16 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter
            pw.println("Available transports:");
            final String[] transports = listAllTransports();
            if (transports != null) {
                for (String t : listAllTransports()) {
                for (String t : transports) {
                    pw.println((t.equals(mTransportManager.getCurrentTransportName()) ? "  * "
                            : "    ") + t);
                    try {
                        IBackupTransport transport = mTransportManager.getTransportBinder(t);
                        File dir = new File(mBaseStateDir, transport.transportDirName());
                        File dir = new File(mBaseStateDir,
                                mTransportManager.getTransportDirName(t));
                        pw.println("       destination: " + transport.currentDestinationString());
                        pw.println("       intent: " + transport.configurationIntent());
                        pw.println("       intent: "
                                + mTransportManager.getTransportConfigurationIntent(t));
                        for (File f : dir.listFiles()) {
                            pw.println(
                                    "       " + f.getName() + " - " + f.length() + " state bytes");
+83 −4
Original line number Diff line number Diff line
@@ -49,12 +49,14 @@ import com.android.server.EventLogTags;
import com.android.server.backup.transport.TransportClient;
import com.android.server.backup.transport.TransportClientManager;
import com.android.server.backup.transport.TransportConnectionListener;
import com.android.server.backup.transport.TransportNotRegisteredException;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate;

/**
@@ -236,6 +238,72 @@ public class TransportManager {
        return getTransportBinder(mCurrentTransportName);
    }

    /**
     * Retrieve the configuration intent of {@code transportName}.
     * @throws TransportNotRegisteredException if the transport is not registered.
     */
    @Nullable
    public Intent getTransportConfigurationIntent(String transportName)
            throws TransportNotRegisteredException {
        synchronized (mTransportLock) {
            return getRegisteredTransportDescriptionOrThrowLocked(transportName)
                    .configurationIntent;
        }
    }

    /**
     * Retrieve the data management intent of {@code transportName}.
     * @throws TransportNotRegisteredException if the transport is not registered.
     */
    @Nullable
    public Intent getTransportDataManagementIntent(String transportName)
            throws TransportNotRegisteredException {
        synchronized (mTransportLock) {
            return getRegisteredTransportDescriptionOrThrowLocked(transportName)
                    .dataManagementIntent;
        }
    }

    /**
     * Retrieve the data management label of {@code transportName}.
     * @throws TransportNotRegisteredException if the transport is not registered.
     */
    @Nullable
    public String getTransportDataManagementLabel(String transportName)
            throws TransportNotRegisteredException {
        synchronized (mTransportLock) {
            return getRegisteredTransportDescriptionOrThrowLocked(transportName)
                    .dataManagementLabel;
        }
    }

    /**
     * Retrieve the transport dir name of {@code transportName}.
     * @throws TransportNotRegisteredException if the transport is not registered.
     */
    public String getTransportDirName(String transportName)
            throws TransportNotRegisteredException {
        synchronized (mTransportLock) {
            return getRegisteredTransportDescriptionOrThrowLocked(transportName)
                    .transportDirName;
        }
    }

    /**
     * Execute {@code transportConsumer} for each registered transport passing the transport name.
     * This is called with an internal lock held, ensuring that the transport will remain registered
     * while {@code transportConsumer} is being executed. Don't do heavy operations in
     * {@code transportConsumer}.
     */
    public void forEachRegisteredTransport(Consumer<String> transportConsumer) {
        synchronized (mTransportLock) {
            for (TransportDescription transportDescription
                    : mRegisteredTransportsDescriptionMap.values()) {
                transportConsumer.accept(transportDescription.name);
            }
        }
    }

    public String getTransportName(IBackupTransport binder) {
        synchronized (mTransportLock) {
            for (TransportConnection conn : mValidTransports.values()) {
@@ -280,6 +348,17 @@ public class TransportManager {
        return (entry == null) ? null : entry.getValue();
    }

    @GuardedBy("mTransportLock")
    private TransportDescription getRegisteredTransportDescriptionOrThrowLocked(
            String transportName) throws TransportNotRegisteredException {
        TransportDescription description = getRegisteredTransportDescriptionLocked(transportName);
        if (description == null) {
            throw new TransportNotRegisteredException(transportName);
        }
        return description;
    }


    @GuardedBy("mTransportLock")
    @Nullable
    private Map.Entry<ComponentName, TransportDescription> getRegisteredTransportEntryLocked(
@@ -385,13 +464,13 @@ public class TransportManager {
     * Updates given values for the transport already registered and identified with
     * {@param transportComponent}. If the transport is not registered it will log and return.
     */
    public void describeTransport(
    public void updateTransportAttributes(
            ComponentName transportComponent,
            String name,
            @Nullable Intent configurationIntent,
            String currentDestinationString,
            @Nullable Intent dataManagementIntent,
            String dataManagementLabel) {
            @Nullable String dataManagementLabel) {
        synchronized (mTransportLock) {
            TransportDescription description =
                    mRegisteredTransportsDescriptionMap.get(transportComponent);
@@ -766,7 +845,7 @@ public class TransportManager {
        @Nullable private Intent configurationIntent;
        private String currentDestinationString;
        @Nullable private Intent dataManagementIntent;
        private String dataManagementLabel;
        @Nullable private String dataManagementLabel;

        private TransportDescription(
                String name,
@@ -774,7 +853,7 @@ public class TransportManager {
                @Nullable Intent configurationIntent,
                String currentDestinationString,
                @Nullable Intent dataManagementIntent,
                String dataManagementLabel) {
                @Nullable String dataManagementLabel) {
            this.name = name;
            this.transportDirName = transportDirName;
            this.configurationIntent = configurationIntent;
+0 −10
Original line number Diff line number Diff line
@@ -293,16 +293,6 @@ public class BackupHandler extends Handler {
                break;
            }

            case MSG_RETRY_INIT: {
                synchronized (backupManagerService.getQueueLock()) {
                    backupManagerService.recordInitPendingLocked(msg.arg1 != 0, (String) msg.obj);
                    backupManagerService.getAlarmManager().set(AlarmManager.RTC_WAKEUP,
                            System.currentTimeMillis(),
                            backupManagerService.getRunInitIntent());
                }
                break;
            }

            case MSG_RUN_GET_RESTORE_SETS: {
                // Like other async operations, this is entered with the wakelock held
                RestoreSet[] sets = null;
+4 −2
Original line number Diff line number Diff line
@@ -98,7 +98,8 @@ public class PerformInitializeTask implements Runnable {
                                    transportDirName));
                    EventLog.writeEvent(EventLogTags.BACKUP_SUCCESS, 0, millis);
                    synchronized (backupManagerService.getQueueLock()) {
                        backupManagerService.recordInitPendingLocked(false, transportName);
                        backupManagerService.recordInitPendingLocked(
                                false, transportName, transportDirName);
                    }
                    notifyResult(transportName, BackupTransport.TRANSPORT_OK);
                } else {
@@ -107,7 +108,8 @@ public class PerformInitializeTask implements Runnable {
                    Slog.e(TAG, "Transport error in initializeDevice()");
                    EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)");
                    synchronized (backupManagerService.getQueueLock()) {
                        backupManagerService.recordInitPendingLocked(true, transportName);
                        backupManagerService.recordInitPendingLocked(
                                true, transportName, transportDirName);
                    }
                    notifyResult(transportName, status);
                    result = status;
+35 −0
Original line number Diff line number Diff line
/*
 * 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.transport;

import android.util.AndroidException;

import com.android.server.backup.TransportManager;

/**
 * Exception thrown when the transport is not registered.
 *
 * @see TransportManager#getTransportDirName(String)
 * @see TransportManager#getTransportConfigurationIntent(String)
 * @see TransportManager#getTransportDataManagementIntent(String)
 * @see TransportManager#getTransportDataManagementLabel(String)
 */
public class TransportNotRegisteredException extends AndroidException {
    public TransportNotRegisteredException(String transportName) {
        super("Transport " + transportName + " not registered");
    }
}
Loading