Merge "DSU to support GSI key revocation list" am: 739c45ea23
Change-Id: Ie2fd14d1fe4029a2c8178c36630167cea0e65af5
This commit is contained in:
@@ -35,4 +35,7 @@
|
|||||||
<!-- Toast when we fail to launch into Dynamic System [CHAR LIMIT=64] -->
|
<!-- Toast when we fail to launch into Dynamic System [CHAR LIMIT=64] -->
|
||||||
<string name="toast_failed_to_reboot_to_dynsystem">Can\u2019t restart or load dynamic system</string>
|
<string name="toast_failed_to_reboot_to_dynsystem">Can\u2019t restart or load dynamic system</string>
|
||||||
|
|
||||||
|
<!-- URL of Dynamic System Key Revocation List [DO NOT TRANSLATE] -->
|
||||||
|
<string name="key_revocation_list_url" translatable="false">https://dl.google.com/developers/android/gsi/gsi-keyblacklist.json</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ import android.app.PendingIntent;
|
|||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.net.http.HttpResponseCache;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
@@ -60,6 +61,8 @@ import android.text.TextUtils;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
@@ -146,10 +149,26 @@ public class DynamicSystemInstallationService extends Service
|
|||||||
prepareNotification();
|
prepareNotification();
|
||||||
|
|
||||||
mDynSystem = (DynamicSystemManager) getSystemService(Context.DYNAMIC_SYSTEM_SERVICE);
|
mDynSystem = (DynamicSystemManager) getSystemService(Context.DYNAMIC_SYSTEM_SERVICE);
|
||||||
|
|
||||||
|
// Install an HttpResponseCache in the application cache directory so we can cache
|
||||||
|
// gsi key revocation list. The http(s) protocol handler uses this cache transparently.
|
||||||
|
// The cache size is chosen heuristically. Since we don't have too much traffic right now,
|
||||||
|
// a moderate size of 1MiB should be enough.
|
||||||
|
try {
|
||||||
|
File httpCacheDir = new File(getCacheDir(), "httpCache");
|
||||||
|
long httpCacheSize = 1 * 1024 * 1024; // 1 MiB
|
||||||
|
HttpResponseCache.install(httpCacheDir, httpCacheSize);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.d(TAG, "HttpResponseCache.install() failed: " + e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
|
HttpResponseCache cache = HttpResponseCache.getInstalled();
|
||||||
|
if (cache != null) {
|
||||||
|
cache.flush();
|
||||||
|
}
|
||||||
// Cancel the persistent notification.
|
// Cancel the persistent notification.
|
||||||
mNM.cancel(NOTIFICATION_ID);
|
mNM.cancel(NOTIFICATION_ID);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ import android.os.image.DynamicSystemManager;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.webkit.URLUtil;
|
import android.webkit.URLUtil;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -100,7 +102,9 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog
|
|||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private final DynamicSystemManager mDynSystem;
|
private final DynamicSystemManager mDynSystem;
|
||||||
private final ProgressListener mListener;
|
private final ProgressListener mListener;
|
||||||
|
private final boolean mIsNetworkUrl;
|
||||||
private DynamicSystemManager.Session mInstallationSession;
|
private DynamicSystemManager.Session mInstallationSession;
|
||||||
|
private KeyRevocationList mKeyRevocationList;
|
||||||
|
|
||||||
private boolean mIsZip;
|
private boolean mIsZip;
|
||||||
private boolean mIsCompleted;
|
private boolean mIsCompleted;
|
||||||
@@ -123,6 +127,7 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog
|
|||||||
mContext = context;
|
mContext = context;
|
||||||
mDynSystem = dynSystem;
|
mDynSystem = dynSystem;
|
||||||
mListener = listener;
|
mListener = listener;
|
||||||
|
mIsNetworkUrl = URLUtil.isNetworkUrl(mUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -152,9 +157,11 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(yochiang): do post-install public key check (revocation list / boot-ramdisk)
|
||||||
|
|
||||||
mDynSystem.finishInstallation();
|
mDynSystem.finishInstallation();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
Log.e(TAG, e.toString(), e);
|
||||||
mDynSystem.remove();
|
mDynSystem.remove();
|
||||||
return e;
|
return e;
|
||||||
} finally {
|
} finally {
|
||||||
@@ -220,7 +227,7 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog
|
|||||||
String.format(Locale.US, "Unsupported file format: %s", mUrl));
|
String.format(Locale.US, "Unsupported file format: %s", mUrl));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (URLUtil.isNetworkUrl(mUrl)) {
|
if (mIsNetworkUrl) {
|
||||||
mStream = new URL(mUrl).openStream();
|
mStream = new URL(mUrl).openStream();
|
||||||
} else if (URLUtil.isFileUrl(mUrl)) {
|
} else if (URLUtil.isFileUrl(mUrl)) {
|
||||||
if (mIsZip) {
|
if (mIsZip) {
|
||||||
@@ -234,6 +241,25 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog
|
|||||||
throw new UnsupportedUrlException(
|
throw new UnsupportedUrlException(
|
||||||
String.format(Locale.US, "Unsupported URL: %s", mUrl));
|
String.format(Locale.US, "Unsupported URL: %s", mUrl));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(yochiang): Bypass this check if device is unlocked
|
||||||
|
try {
|
||||||
|
String listUrl = mContext.getString(R.string.key_revocation_list_url);
|
||||||
|
mKeyRevocationList = KeyRevocationList.fromUrl(new URL(listUrl));
|
||||||
|
} catch (IOException | JSONException e) {
|
||||||
|
Log.d(TAG, "Failed to fetch Dynamic System Key Revocation List");
|
||||||
|
mKeyRevocationList = new KeyRevocationList();
|
||||||
|
keyRevocationThrowOrWarning(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void keyRevocationThrowOrWarning(Exception e) throws Exception {
|
||||||
|
if (mIsNetworkUrl) {
|
||||||
|
throw e;
|
||||||
|
} else {
|
||||||
|
// If DSU is being installed from a local file URI, then be permissive
|
||||||
|
Log.w(TAG, e.toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void installUserdata() throws Exception {
|
private void installUserdata() throws Exception {
|
||||||
|
|||||||
@@ -0,0 +1,148 @@
|
|||||||
|
/*
|
||||||
|
* 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.dynsystem;
|
||||||
|
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLConnection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
class KeyRevocationList {
|
||||||
|
|
||||||
|
private static final String TAG = "KeyRevocationList";
|
||||||
|
|
||||||
|
private static final String JSON_ENTRIES = "entries";
|
||||||
|
private static final String JSON_PUBLIC_KEY = "public_key";
|
||||||
|
private static final String JSON_STATUS = "status";
|
||||||
|
private static final String JSON_REASON = "reason";
|
||||||
|
|
||||||
|
private static final String STATUS_REVOKED = "REVOKED";
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
HashMap<String, RevocationStatus> mEntries;
|
||||||
|
|
||||||
|
static class RevocationStatus {
|
||||||
|
final String mStatus;
|
||||||
|
final String mReason;
|
||||||
|
|
||||||
|
RevocationStatus(String status, String reason) {
|
||||||
|
mStatus = status;
|
||||||
|
mReason = reason;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyRevocationList() {
|
||||||
|
mEntries = new HashMap<String, RevocationStatus>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the revocation status of a public key.
|
||||||
|
*
|
||||||
|
* @return a RevocationStatus for |publicKey|, null if |publicKey| doesn't exist.
|
||||||
|
*/
|
||||||
|
RevocationStatus getRevocationStatusForKey(String publicKey) {
|
||||||
|
return mEntries.get(publicKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Test if a public key is revoked or not. */
|
||||||
|
boolean isRevoked(String publicKey) {
|
||||||
|
RevocationStatus entry = getRevocationStatusForKey(publicKey);
|
||||||
|
return entry != null && TextUtils.equals(entry.mStatus, STATUS_REVOKED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
void addEntry(String publicKey, String status, String reason) {
|
||||||
|
mEntries.put(publicKey, new RevocationStatus(status, reason));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a KeyRevocationList from a JSON String.
|
||||||
|
*
|
||||||
|
* @param jsonString the revocation list, for example:
|
||||||
|
* <pre>{@code
|
||||||
|
* {
|
||||||
|
* "entries": [
|
||||||
|
* {
|
||||||
|
* "public_key": "00fa2c6637c399afa893fe83d85f3569998707d5",
|
||||||
|
* "status": "REVOKED",
|
||||||
|
* "reason": "Revocation Reason"
|
||||||
|
* }
|
||||||
|
* ]
|
||||||
|
* }
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* @throws JSONException if |jsonString| is malformed.
|
||||||
|
*/
|
||||||
|
static KeyRevocationList fromJsonString(String jsonString) throws JSONException {
|
||||||
|
JSONObject jsonObject = new JSONObject(jsonString);
|
||||||
|
KeyRevocationList list = new KeyRevocationList();
|
||||||
|
Log.d(TAG, "Begin of revocation list");
|
||||||
|
if (jsonObject.has(JSON_ENTRIES)) {
|
||||||
|
JSONArray entries = jsonObject.getJSONArray(JSON_ENTRIES);
|
||||||
|
for (int i = 0; i < entries.length(); ++i) {
|
||||||
|
JSONObject entry = entries.getJSONObject(i);
|
||||||
|
String publicKey = entry.getString(JSON_PUBLIC_KEY);
|
||||||
|
String status = entry.getString(JSON_STATUS);
|
||||||
|
String reason = entry.has(JSON_REASON) ? entry.getString(JSON_REASON) : "";
|
||||||
|
list.addEntry(publicKey, status, reason);
|
||||||
|
Log.d(TAG, "Revocation entry: " + entry.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Log.d(TAG, "End of revocation list");
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a KeyRevocationList from a URL.
|
||||||
|
*
|
||||||
|
* @throws IOException if |url| is inaccessible.
|
||||||
|
* @throws JSONException if fetched content is malformed.
|
||||||
|
*/
|
||||||
|
static KeyRevocationList fromUrl(URL url) throws IOException, JSONException {
|
||||||
|
Log.d(TAG, "Fetch from URL: " + url.toString());
|
||||||
|
// Force "conditional GET"
|
||||||
|
// Force validate the cached result with server each time, and use the cached result
|
||||||
|
// only if it is validated by server, else fetch new data from server.
|
||||||
|
// Ref: https://developer.android.com/reference/android/net/http/HttpResponseCache#force-a-network-response
|
||||||
|
URLConnection connection = url.openConnection();
|
||||||
|
connection.setUseCaches(true);
|
||||||
|
connection.addRequestProperty("Cache-Control", "max-age=0");
|
||||||
|
try (InputStream stream = connection.getInputStream()) {
|
||||||
|
return fromJsonString(readFully(stream));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String readFully(InputStream in) throws IOException {
|
||||||
|
int n;
|
||||||
|
byte[] buffer = new byte[4096];
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
while ((n = in.read(buffer, 0, 4096)) > -1) {
|
||||||
|
builder.append(new String(buffer, 0, n));
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
15
packages/DynamicSystemInstallationService/tests/Android.bp
Normal file
15
packages/DynamicSystemInstallationService/tests/Android.bp
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
android_test {
|
||||||
|
name: "DynamicSystemInstallationServiceTests",
|
||||||
|
|
||||||
|
srcs: ["src/**/*.java"],
|
||||||
|
static_libs: [
|
||||||
|
"androidx.test.runner",
|
||||||
|
"androidx.test.rules",
|
||||||
|
"mockito-target-minus-junit4",
|
||||||
|
],
|
||||||
|
|
||||||
|
resource_dirs: ["res"],
|
||||||
|
platform_apis: true,
|
||||||
|
instrumentation_for: "DynamicSystemInstallationService",
|
||||||
|
certificate: "platform",
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="com.android.dynsystem.tests">
|
||||||
|
|
||||||
|
<application>
|
||||||
|
<uses-library android:name="android.test.runner" />
|
||||||
|
</application>
|
||||||
|
|
||||||
|
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
android:targetPackage="com.android.dynsystem"
|
||||||
|
android:label="Tests for DynamicSystemInstallationService" />
|
||||||
|
|
||||||
|
</manifest>
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<!-- testFromJsonString -->
|
||||||
|
<string name="blacklist_json_string" translatable="false">
|
||||||
|
{
|
||||||
|
\"entries\":[
|
||||||
|
{
|
||||||
|
\"public_key\":\"00fa2c6637c399afa893fe83d85f3569998707d5\",
|
||||||
|
\"status\":\"REVOKED\",
|
||||||
|
\"reason\":\"Key revocation test key\"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
\"public_key\":\"key2\",
|
||||||
|
\"status\":\"REVOKED\"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
</string>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
/*
|
||||||
|
* 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.dynsystem;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.doReturn;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.test.filters.SmallTest;
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry;
|
||||||
|
import androidx.test.runner.AndroidJUnit4;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLConnection;
|
||||||
|
import java.net.URLStreamHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A test for KeyRevocationList.java
|
||||||
|
*/
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class KeyRevocationListTest {
|
||||||
|
|
||||||
|
private static final String TAG = "KeyRevocationListTest";
|
||||||
|
|
||||||
|
private static Context sContext;
|
||||||
|
|
||||||
|
private static String sBlacklistJsonString;
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void setUpClass() throws Exception {
|
||||||
|
sContext = InstrumentationRegistry.getInstrumentation().getContext();
|
||||||
|
sBlacklistJsonString =
|
||||||
|
sContext.getString(com.android.dynsystem.tests.R.string.blacklist_json_string);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SmallTest
|
||||||
|
public void testFromJsonString() throws JSONException {
|
||||||
|
KeyRevocationList blacklist;
|
||||||
|
blacklist = KeyRevocationList.fromJsonString(sBlacklistJsonString);
|
||||||
|
Assert.assertNotNull(blacklist);
|
||||||
|
Assert.assertFalse(blacklist.mEntries.isEmpty());
|
||||||
|
blacklist = KeyRevocationList.fromJsonString("{}");
|
||||||
|
Assert.assertNotNull(blacklist);
|
||||||
|
Assert.assertTrue(blacklist.mEntries.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SmallTest
|
||||||
|
public void testFromUrl() throws IOException, JSONException {
|
||||||
|
URLConnection mockConnection = mock(URLConnection.class);
|
||||||
|
doReturn(new ByteArrayInputStream(sBlacklistJsonString.getBytes()))
|
||||||
|
.when(mockConnection).getInputStream();
|
||||||
|
URL mockUrl = new URL(
|
||||||
|
"http", // protocol
|
||||||
|
"foo.bar", // host
|
||||||
|
80, // port
|
||||||
|
"baz", // file
|
||||||
|
new URLStreamHandler() {
|
||||||
|
@Override
|
||||||
|
protected URLConnection openConnection(URL url) {
|
||||||
|
return mockConnection;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
URL mockBadUrl = new URL(
|
||||||
|
"http", // protocol
|
||||||
|
"foo.bar", // host
|
||||||
|
80, // port
|
||||||
|
"baz", // file
|
||||||
|
new URLStreamHandler() {
|
||||||
|
@Override
|
||||||
|
protected URLConnection openConnection(URL url) throws IOException {
|
||||||
|
throw new IOException();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
KeyRevocationList blacklist = KeyRevocationList.fromUrl(mockUrl);
|
||||||
|
Assert.assertNotNull(blacklist);
|
||||||
|
Assert.assertFalse(blacklist.mEntries.isEmpty());
|
||||||
|
|
||||||
|
blacklist = null;
|
||||||
|
try {
|
||||||
|
blacklist = KeyRevocationList.fromUrl(mockBadUrl);
|
||||||
|
// Up should throw, down should be unreachable
|
||||||
|
Assert.fail("Expected IOException not thrown");
|
||||||
|
} catch (IOException e) {
|
||||||
|
// This is expected, do nothing
|
||||||
|
}
|
||||||
|
Assert.assertNull(blacklist);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SmallTest
|
||||||
|
public void testIsRevoked() {
|
||||||
|
KeyRevocationList blacklist = new KeyRevocationList();
|
||||||
|
blacklist.addEntry("key1", "REVOKED", "reason for key1");
|
||||||
|
|
||||||
|
KeyRevocationList.RevocationStatus revocationStatus =
|
||||||
|
blacklist.getRevocationStatusForKey("key1");
|
||||||
|
Assert.assertNotNull(revocationStatus);
|
||||||
|
Assert.assertEquals(revocationStatus.mReason, "reason for key1");
|
||||||
|
|
||||||
|
revocationStatus = blacklist.getRevocationStatusForKey("key2");
|
||||||
|
Assert.assertNull(revocationStatus);
|
||||||
|
|
||||||
|
Assert.assertTrue(blacklist.isRevoked("key1"));
|
||||||
|
Assert.assertFalse(blacklist.isRevoked("key2"));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user