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

Commit 1f67242c authored by Richard Uhler's avatar Richard Uhler
Browse files

Add test for user data rollback.

The test is currently marked @Ignore because we haven't implemented
support for user data rollback yet.

Test: atest RollbackTest (selinux disabled)
Test: atest RollbackTest fails without the @Ignore.
Bug: 112431924
Change-Id: I3f7906001c932969bdf2125d198f379bf35a99fb
parent e95d055c
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -23,6 +23,9 @@
    <uses-sdk android:minSdkVersion="19" />

    <application android:label="Rollback Test App V1">
        <meta-data android:name="version" android:value="1" />
        <receiver android:name="com.android.tests.rollback.testapp.ProcessUserData"
                  android:exported="true" />
        <activity android:name="com.android.tests.rollback.testapp.MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
+3 −0
Original line number Diff line number Diff line
@@ -23,6 +23,9 @@
    <uses-sdk android:minSdkVersion="19" />

    <application android:label="Rollback Test App V2">
        <meta-data android:name="version" android:value="2" />
        <receiver android:name="com.android.tests.rollback.testapp.ProcessUserData"
                  android:exported="true" />
        <activity android:name="com.android.tests.rollback.testapp.MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
+6 −0
Original line number Diff line number Diff line
@@ -27,5 +27,11 @@ public class MainActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        try {
            new ProcessUserData().processUserData(this);
        } catch (ProcessUserData.UserDataException e) {
            throw new AssertionError("Failed to process app user data", e);
        }
    }
}
+106 −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.tests.rollback.testapp;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Scanner;

/**
 * A broadcast reciever to check for and update user app data version
 * compatibility.
 */
public class ProcessUserData extends BroadcastReceiver {

    /**
     * Exception thrown in case of issue with user data.
     */
    public static class UserDataException extends Exception {
        public UserDataException(String message) {
            super(message);
        }

        public UserDataException(String message, Throwable cause) {
           super(message, cause);
        }
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        try {
            processUserData(context);
            setResultCode(1);
        } catch (UserDataException e) {
            setResultCode(0);
            setResultData(e.getMessage());
        }
    }

    /**
     * Update the app's user data version to match the app version.
     *
     * @param context The application context.
     * @throws UserDataException in case of problems with app user data.
     */
    public void processUserData(Context context) throws UserDataException {
        int appVersion = 0;
        try {
            ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(
                    context.getPackageName(), PackageManager.GET_META_DATA);
            Bundle bundle = appInfo.metaData;
            appVersion = bundle.getInt("version");
        } catch (PackageManager.NameNotFoundException e) {
            throw new UserDataException("Unable to get app version info", e);
        }

        // Read the version of the app's user data and ensure it is compatible
        // with our version of the application.
        File versionFile = new File(context.getFilesDir(), "version.txt");
        try {
            Scanner s = new Scanner(versionFile);
            int userDataVersion = s.nextInt();
            s.close();

            if (userDataVersion > appVersion) {
                throw new UserDataException("User data is from version " + userDataVersion
                        + ", which is not compatible with this version " + appVersion
                        + " of the RollbackTestApp");
            }
        } catch (FileNotFoundException e) {
            // No problem. This is a fresh install of the app or the user data
            // has been wiped.
        }

        // Record the current version of the app in the user data.
        try {
            PrintWriter pw = new PrintWriter(versionFile);
            pw.println(appVersion);
            pw.close();
        } catch (IOException e) {
            throw new UserDataException("Unable to write user data.", e);
        }
    }
}
+80 −0
Original line number Diff line number Diff line
@@ -18,12 +18,15 @@ package com.android.tests.rollback;

import android.Manifest;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.rollback.RollbackInfo;
import android.content.rollback.RollbackManager;
import android.net.Uri;
import android.os.Handler;
import android.os.HandlerThread;
import android.support.test.InstrumentationRegistry;
import android.util.Log;

@@ -35,10 +38,13 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

/**
@@ -274,6 +280,80 @@ public class RollbackTest {
        }
    }

    private static final String NO_RESPONSE = "NO RESPONSE";

    // Calls into the test app to process user data.
    // Asserts if the user data could not be processed or was version
    // incompatible with the previously processed user data.
    private void processUserData() throws Exception {
        Intent intent = new Intent();
        intent.setComponent(new ComponentName(
                    "com.android.tests.rollback.testapp",
                    "com.android.tests.rollback.testapp.ProcessUserData"));
        Context context = InstrumentationRegistry.getContext();

        HandlerThread handlerThread = new HandlerThread("RollbackTestHandlerThread");
        handlerThread.start();

        // It can sometimes take a while after rollback before the app will
        // receive this broadcast, so try a few times in a loop.
        String result = NO_RESPONSE;
        for (int i = 0; result.equals(NO_RESPONSE) && i < 5; ++i) {
            BlockingQueue<String> resultQueue = new LinkedBlockingQueue<>();
            context.sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
                @Override
                public void onReceive(Context context, Intent intent) {
                    if (getResultCode() == 1) {
                        resultQueue.add("OK");
                    } else {
                        // If the test app doesn't receive the broadcast or
                        // fails to set the result data, then getResultData
                        // here returns the initial NO_RESPONSE data passed to
                        // the sendOrderedBroadcast call.
                        resultQueue.add(getResultData());
                    }
                }
            }, new Handler(handlerThread.getLooper()), 0, NO_RESPONSE, null);

            result = resultQueue.poll(10, TimeUnit.SECONDS);
            if (result == null) {
                result = "ProcessUserData broadcast timed out";
            }
        }

        handlerThread.quit();
        if (!"OK".equals(result)) {
            fail(result);
        }
    }

    /**
     * Test that app user data is rolled back.
     * TODO: Stop ignoring this test once user data rollback is supported.
     */
    @Ignore @Test
    public void testUserDataRollback() throws Exception {
        try {
            RollbackTestUtils.adoptShellPermissionIdentity(
                    Manifest.permission.INSTALL_PACKAGES,
                    Manifest.permission.DELETE_PACKAGES,
                    Manifest.permission.MANAGE_ROLLBACKS);

            RollbackTestUtils.uninstall("com.android.tests.rollback.testapp");
            RollbackTestUtils.install("RollbackTestAppV1.apk", false);
            processUserData();
            RollbackTestUtils.install("RollbackTestAppV2.apk", true);
            processUserData();

            RollbackManager rm = RollbackTestUtils.getRollbackManager();
            RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_PACKAGE_NAME);
            RollbackTestUtils.rollback(rollback);
            processUserData();
        } finally {
            RollbackTestUtils.dropShellPermissionIdentity();
        }
    }

    /**
     * Test restrictions on rollback broadcast sender.
     * A random app should not be able to send a PACKAGE_ROLLBACK_EXECUTED broadcast.