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

Commit 5169896d authored by Ruslan Tkhakokhov's avatar Ruslan Tkhakokhov
Browse files

Partially delete unused EITF code

Delete the unused code that was written in 2019 as part of the
Encryption in the Framework project. This change is preparatory to
making the backup transport AIDL async and avoids having to implement
async communication for dead code.

Bug: 202716271
Test: m -j
Change-Id: I57cbb9e6a7d3350e1a278c60fced6618018b3488
parent f6ca9038
Loading
Loading
Loading
Loading
+0 −63
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2019 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.encryption;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;

import com.android.internal.backup.IBackupTransport;
import com.android.server.backup.encryption.transport.IntermediateEncryptingTransport;
import com.android.server.backup.encryption.transport.IntermediateEncryptingTransportManager;

/**
 * This service provides encryption of backup data. For an intent used to bind to this service, it
 * provides an {@link IntermediateEncryptingTransport} which is an implementation of {@link
 * IBackupTransport} that encrypts (or decrypts) the data when sending it (or receiving it) from the
 * real {@link IBackupTransport}.
 */
public class BackupEncryptionService extends Service {
    public static final String TAG = "BackupEncryption";
    private static IntermediateEncryptingTransportManager sTransportManager = null;

    @Override
    public void onCreate() {
        Log.i(TAG, "onCreate:" + this);
        if (sTransportManager == null) {
            Log.i(TAG, "Creating IntermediateEncryptingTransportManager");
            sTransportManager = new IntermediateEncryptingTransportManager(this);
        }
    }

    @Override
    public void onDestroy() {
        Log.i(TAG, "onDestroy:" + this);
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO (b141536117): Check connection with TransportClient.connect and return null on fail.
        return sTransportManager.get(intent);
    }

    @Override
    public boolean onUnbind(Intent intent) {
        sTransportManager.cleanup(intent);
        return false;
    }
}
+2 −2
Original line number Original line Diff line number Diff line
@@ -16,8 +16,6 @@


package com.android.server.backup.encryption;
package com.android.server.backup.encryption;


import static com.android.server.backup.encryption.BackupEncryptionService.TAG;

import android.content.Context;
import android.content.Context;
import android.os.ParcelFileDescriptor;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import android.util.Log;
@@ -36,6 +34,8 @@ import java.security.SecureRandom;
import java.util.Map;
import java.util.Map;


public class KeyValueEncrypter {
public class KeyValueEncrypter {
    private static final String TAG = "KeyValueEncrypter";

    private final Context mContext;
    private final Context mContext;
    private final EncryptionKeyHelper mKeyHelper;
    private final EncryptionKeyHelper mKeyHelper;


+0 −211
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2019 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.encryption.transport;

import static com.android.server.backup.encryption.BackupEncryptionService.TAG;

import android.app.backup.BackupTransport;
import android.app.backup.RestoreDescription;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.util.Log;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.backup.IBackupTransport;
import com.android.server.backup.encryption.KeyValueEncrypter;
import com.android.server.backup.transport.DelegatingTransport;
import com.android.server.backup.transport.TransportClient;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.atomic.AtomicReference;

/**
 * This is an implementation of {@link IBackupTransport} that encrypts (or decrypts) the data when
 * sending it (or receiving it) from the {@link IBackupTransport} returned by {@link
 * TransportClient.connect(String)}.
 */
public class IntermediateEncryptingTransport extends DelegatingTransport {
    private static final String BACKUP_TEMP_DIR = "backup";
    private static final String RESTORE_TEMP_DIR = "restore";

    private final TransportClient mTransportClient;
    private final Object mConnectLock = new Object();
    private final Context mContext;
    private volatile IBackupTransport mRealTransport;
    private AtomicReference<String> mNextRestorePackage = new AtomicReference<>();
    private final KeyValueEncrypter mKeyValueEncrypter;
    private final boolean mShouldEncrypt;

    IntermediateEncryptingTransport(
            TransportClient transportClient, Context context, boolean shouldEncrypt) {
        this(transportClient, context, new KeyValueEncrypter(context), shouldEncrypt);
    }

    @VisibleForTesting
    IntermediateEncryptingTransport(
            TransportClient transportClient, Context context, KeyValueEncrypter keyValueEncrypter,
            boolean shouldEncrypt) {
        mTransportClient = transportClient;
        mContext = context;
        mKeyValueEncrypter = keyValueEncrypter;
        mShouldEncrypt = shouldEncrypt;
    }

    @Override
    protected IBackupTransport getDelegate() throws RemoteException {
        if (mRealTransport == null) {
            connect();
        }
        Log.d(TAG, "real transport = " + mRealTransport.name());
        return mRealTransport;
    }

    @Override
    public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor inFd, int flags)
            throws RemoteException {
        if (!mShouldEncrypt) {
            return super.performBackup(packageInfo, inFd, flags);
        }

        File encryptedStorageFile = getBackupTempStorage(packageInfo.packageName);
        if (encryptedStorageFile == null) {
            return BackupTransport.TRANSPORT_ERROR;
        }

        // Encrypt the backup data and write it into a temp file.
        try (OutputStream encryptedOutput = new FileOutputStream(encryptedStorageFile)) {
            mKeyValueEncrypter.encryptKeyValueData(packageInfo.packageName, inFd,
                    encryptedOutput);
        } catch (Throwable e) {
            Log.e(TAG, "Failed to encrypt backup data: ", e);
            return BackupTransport.TRANSPORT_ERROR;
        }

        // Pass the temp file to the real transport for backup.
        try (FileInputStream encryptedInput = new FileInputStream(encryptedStorageFile)) {
            return super.performBackup(
                    packageInfo, ParcelFileDescriptor.dup(encryptedInput.getFD()), flags);
        } catch (IOException e) {
            Log.e(TAG, "Failed to read encrypted data from temp storage: ", e);
            return BackupTransport.TRANSPORT_ERROR;
        }
    }

    @Override
    public int getRestoreData(ParcelFileDescriptor outFd) throws RemoteException {
        if (!mShouldEncrypt) {
            return super.getRestoreData(outFd);
        }

        String nextRestorePackage = mNextRestorePackage.get();
        if (nextRestorePackage == null) {
            Log.e(TAG, "No next restore package set");
            return BackupTransport.TRANSPORT_ERROR;
        }

        File encryptedStorageFile = getRestoreTempStorage(nextRestorePackage);
        if (encryptedStorageFile == null) {
            return BackupTransport.TRANSPORT_ERROR;
        }

        // Get encrypted restore data from the real transport and write it into a temp file.
        try (FileOutputStream outputStream = new FileOutputStream(encryptedStorageFile)) {
            int status = super.getRestoreData(ParcelFileDescriptor.dup(outputStream.getFD()));
            if (status != BackupTransport.TRANSPORT_OK) {
                Log.e(TAG, "Failed to read restore data from transport, status = " + status);
                return status;
            }
        } catch (IOException e) {
            Log.e(TAG, "Failed to write encrypted data to temp storage: ", e);
            return BackupTransport.TRANSPORT_ERROR;
        }

        // Decrypt the data and write it into the fd given by the real transport.
        try (InputStream inputStream = new FileInputStream(encryptedStorageFile)) {
            mKeyValueEncrypter.decryptKeyValueData(nextRestorePackage, inputStream, outFd);
            encryptedStorageFile.delete();
        } catch (Exception e) {
            Log.e(TAG, "Failed to decrypt restored data: ", e);
            return BackupTransport.TRANSPORT_ERROR;
        }

        return BackupTransport.TRANSPORT_OK;
    }

    @Override
    public RestoreDescription nextRestorePackage() throws RemoteException {
        if (!mShouldEncrypt) {
            return super.nextRestorePackage();
        }

        RestoreDescription restoreDescription = super.nextRestorePackage();
        mNextRestorePackage.set(restoreDescription.getPackageName());

        return restoreDescription;
    }

    @VisibleForTesting
    protected File getBackupTempStorage(String packageName) {
        return getTempStorage(packageName, BACKUP_TEMP_DIR);
    }

    @VisibleForTesting
    protected File getRestoreTempStorage(String packageName) {
        return getTempStorage(packageName, RESTORE_TEMP_DIR);
    }

    private File getTempStorage(String packageName, String operationType) {
        File encryptedDir = new File(mContext.getFilesDir(), operationType);
        encryptedDir.mkdir();
        File encryptedFile = new File(encryptedDir, packageName);
        try {
            encryptedFile.createNewFile();
        } catch (IOException e) {
            Log.e(TAG, "Failed to create temp file for encrypted data: ", e);
        }
        return encryptedFile;
    }

    private void connect() throws RemoteException {
        Log.i(TAG, "connecting " + mTransportClient);
        synchronized (mConnectLock) {
            if (mRealTransport == null) {
                mRealTransport = mTransportClient.connect("IntermediateEncryptingTransport");
                if (mRealTransport == null) {
                    throw new RemoteException("Could not connect: " + mTransportClient);
                }
            }
        }
    }

    @VisibleForTesting
    TransportClient getClient() {
        return mTransportClient;
    }

    @VisibleForTesting
    void setNextRestorePackage(String nextRestorePackage) {
        mNextRestorePackage.set(nextRestorePackage);
    }
}
+0 −110
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2019 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.encryption.transport;

import static com.android.server.backup.encryption.BackupEncryptionService.TAG;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.UserHandle;
import android.util.Log;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.backup.IBackupTransport;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.backup.transport.TransportClientManager;
import com.android.server.backup.transport.TransportStats;

import java.util.HashMap;
import java.util.Map;

/** Handles creation and cleanup of {@link IntermediateEncryptingTransport} instances. */
public class IntermediateEncryptingTransportManager {
    private static final String CALLER = "IntermediateEncryptingTransportManager";
    private final TransportClientManager mTransportClientManager;
    private final Object mTransportsLock = new Object();
    private final Map<ComponentName, IntermediateEncryptingTransport> mTransports = new HashMap<>();
    private Context mContext;

    @VisibleForTesting
    IntermediateEncryptingTransportManager(TransportClientManager transportClientManager) {
        mTransportClientManager = transportClientManager;
    }

    public IntermediateEncryptingTransportManager(Context context) {
        this(new TransportClientManager(UserHandle.myUserId(), context, new TransportStats()));
        mContext = context;
    }

    /**
     * Extract the {@link ComponentName} corresponding to the real {@link IBackupTransport}, and
     * provide a {@link IntermediateEncryptingTransport} which is an implementation of {@link
     * IBackupTransport} that encrypts (or decrypts) the data when sending it (or receiving it) from
     * the real {@link IBackupTransport}.
     *
     * @param intent {@link Intent} created with a call to {@link
     *     TransportClientManager.getEncryptingTransportIntent(ComponentName)}.
     * @return
     */
    public IntermediateEncryptingTransport get(Intent intent) {
        Intent transportIntent = TransportClientManager.getRealTransportIntent(intent);
        Log.i(TAG, "get: intent:" + intent + " transportIntent:" + transportIntent);
        synchronized (mTransportsLock) {
            return mTransports.computeIfAbsent(
                    transportIntent.getComponent(), c -> create(transportIntent));
        }
    }

    /** Create an instance of {@link IntermediateEncryptingTransport}. */
    private IntermediateEncryptingTransport create(Intent realTransportIntent) {
        Log.d(TAG, "create: intent:" + realTransportIntent);

        LockPatternUtils patternUtils = new LockPatternUtils(mContext);
        boolean shouldEncrypt =
                realTransportIntent.getComponent().getClassName().contains("EncryptedLocalTransport")
                        && (patternUtils.isLockPatternEnabled(UserHandle.myUserId())
                                || patternUtils.isLockPasswordEnabled(UserHandle.myUserId()));

        return new IntermediateEncryptingTransport(
                mTransportClientManager.getTransportClient(
                        realTransportIntent.getComponent(),
                        realTransportIntent.getExtras(),
                        CALLER),
                mContext,
                shouldEncrypt);
    }

    /**
     * Cleanup the {@link IntermediateEncryptingTransport} which was created by a call to {@link
     * #get(Intent)} with this {@link Intent}.
     */
    public void cleanup(Intent intent) {
        Intent transportIntent = TransportClientManager.getRealTransportIntent(intent);
        Log.i(TAG, "cleanup: intent:" + intent + " transportIntent:" + transportIntent);

        IntermediateEncryptingTransport transport;
        synchronized (mTransportsLock) {
            transport = mTransports.remove(transportIntent.getComponent());
        }
        if (transport != null) {
            mTransportClientManager.disposeOfTransportClient(transport.getClient(), CALLER);
        } else {
            Log.i(TAG, "Could not find IntermediateEncryptingTransport");
        }
    }
}
+0 −120
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2019 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.encryption.transport;

import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotSame;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;

import android.content.ComponentName;
import android.content.Intent;
import android.os.Bundle;
import android.platform.test.annotations.Presubmit;

import androidx.test.runner.AndroidJUnit4;

import com.android.server.backup.transport.TransportClient;
import com.android.server.backup.transport.TransportClientManager;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

@Presubmit
@RunWith(AndroidJUnit4.class)
public class IntermediateEncryptingTransportManagerTest {
    @Mock private TransportClient mTransportClient;
    @Mock private TransportClientManager mTransportClientManager;

    private final ComponentName mTransportComponent = new ComponentName("pkg", "class");
    private final Bundle mExtras = new Bundle();
    private Intent mEncryptingTransportIntent;
    private IntermediateEncryptingTransportManager mIntermediateEncryptingTransportManager;

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
        mExtras.putInt("test", 1);
        mEncryptingTransportIntent =
                TransportClientManager.getEncryptingTransportIntent(mTransportComponent)
                        .putExtras(mExtras);
        mIntermediateEncryptingTransportManager =
                new IntermediateEncryptingTransportManager(mTransportClientManager);
    }

    @Test
    public void testGet_createsClientWithRealTransportComponentAndExtras() {
        when(mTransportClientManager.getTransportClient(any(), any(), any()))
                .thenReturn(mTransportClient);

        IntermediateEncryptingTransport intermediateEncryptingTransport =
                mIntermediateEncryptingTransportManager.get(mEncryptingTransportIntent);

        assertEquals(mTransportClient, intermediateEncryptingTransport.getClient());
        verify(mTransportClientManager, times(1))
                .getTransportClient(eq(mTransportComponent), argThat(mExtras::kindofEquals), any());
        verifyNoMoreInteractions(mTransportClientManager);
    }

    @Test
    public void testGet_callTwice_returnsSameTransport() {
        IntermediateEncryptingTransport transport1 =
                mIntermediateEncryptingTransportManager.get(mEncryptingTransportIntent);
        IntermediateEncryptingTransport transport2 =
                mIntermediateEncryptingTransportManager.get(mEncryptingTransportIntent);

        assertEquals(transport1, transport2);
    }

    @Test
    public void testCleanup_disposesTransportClient() {
        when(mTransportClientManager.getTransportClient(any(), any(), any()))
                .thenReturn(mTransportClient);

        IntermediateEncryptingTransport transport =
                mIntermediateEncryptingTransportManager.get(mEncryptingTransportIntent);
        mIntermediateEncryptingTransportManager.cleanup(mEncryptingTransportIntent);

        verify(mTransportClientManager, times(1)).getTransportClient(any(), any(), any());
        verify(mTransportClientManager, times(1))
                .disposeOfTransportClient(eq(mTransportClient), any());
        verifyNoMoreInteractions(mTransportClientManager);
    }

    @Test
    public void testCleanup_removesCachedTransport() {
        when(mTransportClientManager.getTransportClient(any(), any(), any()))
                .thenReturn(mTransportClient);

        IntermediateEncryptingTransport transport1 =
                mIntermediateEncryptingTransportManager.get(mEncryptingTransportIntent);
        mIntermediateEncryptingTransportManager.cleanup(mEncryptingTransportIntent);
        IntermediateEncryptingTransport transport2 =
                mIntermediateEncryptingTransportManager.get(mEncryptingTransportIntent);

        assertNotSame(transport1, transport2);
    }
}
Loading