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

Commit 1f1bae90 authored by Bernardo Rufino's avatar Bernardo Rufino
Browse files

[KV] Exceptions for error-handling

Added 2 exceptions:
* TaskException: For what we used to call transport-level failures,
  these bring queue processing to a halt.
* AgentException: For failures that happen due to the backup agent, these
  only prevent the backup of the current package, still allowing backup
  for the remaining packages in the queue.

These are usually thrown deep in the call stack and caught in backupPackage()
(and similarly backupPm()) and run(), the former for clean-up, where
they are re-thrown and the latter for further processing where they are
finally swallowed. The clean-up is more explicit now.

This enabled further refactoring of backupPackage()/backupPm(),
extractAgentData() and sendDataToTransport().

One change that I intend to revisit is reporting to the observer.
Previously we used to detect some exceptional cases and set mStatus
(which doesn't exist anymore) to other more general exceptional case and
then in this general case handling report the success/failure to the observer.
With the new exception-throwing model I changed this and the leaves are
actually responsible for reporting success/failure to the observer (see
changes in the reporter). This is to avoid too many changes in this CL.

I'm entertaining the idea of extracting out package backup as a separate
class and leave only queue-processing and generic bookkeeping to this
class.

Test: atest FrameworksServicesRoboTests
Test: adb shell bmgr backupnow <kv_package>
Change-Id: If3b1da7a5eb939e453f94fad76f5790696d1265a
parent 3921b96e
Loading
Loading
Loading
Loading
+63 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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.keyvalue;

/**
 * This represents something wrong with a specific package. For example:
 * <ul>
 *     <li>Package unknown.
 *     <li>Package is not eligible for backup anymore.
 *     <li>Backup agent timed out.
 *     <li>Backup agent wrote protected keys.
 *     <li>...
 * </ul>
 *
 * @see KeyValueBackupTask
 * @see TaskException
 */
class AgentException extends BackupException {
    static AgentException transitory() {
        return new AgentException(/* transitory */ true);
    }

    static AgentException transitory(Exception cause) {
        return new AgentException(/* transitory */ true, cause);
    }

    static AgentException permanent() {
        return new AgentException(/* transitory */ false);
    }

    static AgentException permanent(Exception cause) {
        return new AgentException(/* transitory */ false, cause);
    }

    private final boolean mTransitory;

    private AgentException(boolean transitory) {
        mTransitory = transitory;
    }

    private AgentException(boolean transitory, Exception cause) {
        super(cause);
        mTransitory = transitory;
    }

    boolean isTransitory() {
        return mTransitory;
    }
}
+33 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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.keyvalue;

import android.util.AndroidException;

/**
 * Key-value backup task exception.
 *
 * @see AgentException
 * @see TaskException
 */
class BackupException extends AndroidException {
    BackupException() {}

    BackupException(Exception cause) {
        super(cause);
    }
}
+19 −16
Original line number Diff line number Diff line
@@ -153,16 +153,18 @@ public class KeyValueBackupReporter {
                mObserver, packageName, BackupManager.ERROR_BACKUP_NOT_ALLOWED);
    }

    void onBindAgentError(SecurityException e) {
        Slog.d(TAG, "Error in bind/backup", e);
    }

    void onAgentUnknown(String packageName) {
        Slog.d(TAG, "Package does not exist, skipping");
        BackupObserverUtils.sendBackupOnPackageResult(
                mObserver, packageName, BackupManager.ERROR_PACKAGE_NOT_FOUND);
    }

    void onBindAgentError(String packageName, SecurityException e) {
        Slog.d(TAG, "Error in bind/backup", e);
        BackupObserverUtils.sendBackupOnPackageResult(
                mObserver, packageName, BackupManager.ERROR_AGENT_FAILURE);
    }

    void onAgentError(String packageName) {
        if (MORE_DEBUG) {
            Slog.i(TAG, "Agent failure for " + packageName + ", re-staging");
@@ -190,6 +192,8 @@ public class KeyValueBackupReporter {
    void onCallAgentDoBackupError(String packageName, boolean callingAgent, Exception e) {
        if (callingAgent) {
            Slog.e(TAG, "Error invoking agent on " + packageName + ": " + e);
            BackupObserverUtils.sendBackupOnPackageResult(
                    mObserver, packageName, BackupManager.ERROR_AGENT_FAILURE);
        } else {
            Slog.e(TAG, "Error before invoking agent on " + packageName + ": " + e);
        }
@@ -220,12 +224,8 @@ public class KeyValueBackupReporter {
        }
    }

    void onReadAgentDataError(String packageName, IOException e) {
        Slog.w(TAG, "Unable read backup data for " + packageName + ": " + e);
    }

    void onWriteWidgetDataError(String packageName, IOException e) {
        Slog.w(TAG, "Unable to save widget data for " + packageName + ": " + e);
    void onAgentDataError(String packageName, IOException e) {
        Slog.w(TAG, "Unable to read/write agent data for " + packageName + ": " + e);
    }

    void onDigestError(NoSuchAlgorithmException e) {
@@ -243,16 +243,12 @@ public class KeyValueBackupReporter {
        }
    }

    void onSendDataToTransport(String packageName) {
    void onTransportPerformBackup(String packageName) {
        if (MORE_DEBUG) {
            Slog.v(TAG, "Sending non-empty data to transport for " + packageName);
        }
    }

    void onNonIncrementalAndNonIncrementalRequired() {
        Slog.e(TAG, "Transport requested non-incremental but already the case");
    }

    void onEmptyData(PackageInfo packageInfo) {
        if (MORE_DEBUG) {
            Slog.i(TAG, "No backup data written, not calling transport");
@@ -302,13 +298,20 @@ public class KeyValueBackupReporter {
                /* extras */ null);
    }

    void onPackageBackupNonIncrementalAndNonIncrementalRequired(String packageName) {
        Slog.e(TAG, "Transport requested non-incremental but already the case");
        BackupObserverUtils.sendBackupOnPackageResult(
                mObserver, packageName, BackupManager.ERROR_TRANSPORT_ABORTED);
        EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, packageName);
    }

    void onPackageBackupTransportFailure(String packageName) {
        BackupObserverUtils.sendBackupOnPackageResult(
                mObserver, packageName, BackupManager.ERROR_TRANSPORT_ABORTED);
        EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, packageName);
    }

    void onPackageBackupError(String packageName, Exception e) {
    void onPackageBackupTransportError(String packageName, Exception e) {
        Slog.e(TAG, "Transport error backing up " + packageName, e);
        BackupObserverUtils.sendBackupOnPackageResult(
                mObserver, packageName, BackupManager.ERROR_TRANSPORT_ABORTED);
+312 −290

File changed.

Preview size limit exceeded, changes collapsed.

+83 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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.keyvalue;

import android.app.backup.BackupTransport;

import com.android.internal.util.Preconditions;

/**
 * The key-value backup task has failed, no more packages will be processed and we shouldn't attempt
 * any more backups now. These can be caused by transport failures (as opposed to agent failures).
 *
 * @see KeyValueBackupTask
 * @see AgentException
 */
class TaskException extends BackupException {
    private static final int DEFAULT_STATUS = BackupTransport.TRANSPORT_ERROR;

    static TaskException stateCompromised() {
        return new TaskException(/* stateCompromised */ true, DEFAULT_STATUS);
    }

    static TaskException stateCompromised(Exception cause) {
        if (cause instanceof TaskException) {
            TaskException exception = (TaskException) cause;
            return new TaskException(cause, /* stateCompromised */ true, exception.getStatus());
        }
        return new TaskException(cause, /* stateCompromised */ true, DEFAULT_STATUS);
    }

    static TaskException forStatus(int status) {
        Preconditions.checkArgument(
                status != BackupTransport.TRANSPORT_OK, "Exception based on TRANSPORT_OK");
        return new TaskException(/* stateCompromised */ false, status);
    }

    static TaskException causedBy(Exception cause) {
        if (cause instanceof TaskException) {
            return (TaskException) cause;
        }
        return new TaskException(cause, /* stateCompromised */ false, DEFAULT_STATUS);
    }

    static TaskException create() {
        return new TaskException(/* stateCompromised */ false, DEFAULT_STATUS);
    }

    private final boolean mStateCompromised;
    private final int mStatus;

    private TaskException(Exception cause, boolean stateCompromised, int status) {
        super(cause);
        mStateCompromised = stateCompromised;
        mStatus = status;
    }

    private TaskException(boolean stateCompromised, int status) {
        mStateCompromised = stateCompromised;
        mStatus = status;
    }

    boolean isStateCompromised() {
        return mStateCompromised;
    }

    int getStatus() {
        return mStatus;
    }
}
Loading