diff options
33 files changed, 1258 insertions, 198 deletions
diff --git a/api/current.txt b/api/current.txt index 21fe3e2febc4..21ff787df176 100644 --- a/api/current.txt +++ b/api/current.txt @@ -62850,7 +62850,10 @@ package java.util.zip { method public java.lang.String getComment(); method public long getCompressedSize(); method public long getCrc(); + method public java.nio.file.attribute.FileTime getCreationTime(); method public byte[] getExtra(); + method public java.nio.file.attribute.FileTime getLastAccessTime(); + method public java.nio.file.attribute.FileTime getLastModifiedTime(); method public int getMethod(); method public java.lang.String getName(); method public long getSize(); @@ -62859,7 +62862,10 @@ package java.util.zip { method public void setComment(java.lang.String); method public void setCompressedSize(long); method public void setCrc(long); + method public java.util.zip.ZipEntry setCreationTime(java.nio.file.attribute.FileTime); method public void setExtra(byte[]); + method public java.util.zip.ZipEntry setLastAccessTime(java.nio.file.attribute.FileTime); + method public java.util.zip.ZipEntry setLastModifiedTime(java.nio.file.attribute.FileTime); method public void setMethod(int); method public void setSize(long); method public void setTime(long); @@ -62930,6 +62936,7 @@ package java.util.zip { method public java.io.InputStream getInputStream(java.util.zip.ZipEntry) throws java.io.IOException; method public java.lang.String getName(); method public int size(); + method public java.util.stream.Stream<? extends java.util.zip.ZipEntry> stream(); field public static final int CENATT = 36; // 0x24 field public static final int CENATX = 38; // 0x26 field public static final int CENCOM = 32; // 0x20 diff --git a/api/system-current.txt b/api/system-current.txt index 974db45e2430..6645d53613b1 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -66207,7 +66207,10 @@ package java.util.zip { method public java.lang.String getComment(); method public long getCompressedSize(); method public long getCrc(); + method public java.nio.file.attribute.FileTime getCreationTime(); method public byte[] getExtra(); + method public java.nio.file.attribute.FileTime getLastAccessTime(); + method public java.nio.file.attribute.FileTime getLastModifiedTime(); method public int getMethod(); method public java.lang.String getName(); method public long getSize(); @@ -66216,7 +66219,10 @@ package java.util.zip { method public void setComment(java.lang.String); method public void setCompressedSize(long); method public void setCrc(long); + method public java.util.zip.ZipEntry setCreationTime(java.nio.file.attribute.FileTime); method public void setExtra(byte[]); + method public java.util.zip.ZipEntry setLastAccessTime(java.nio.file.attribute.FileTime); + method public java.util.zip.ZipEntry setLastModifiedTime(java.nio.file.attribute.FileTime); method public void setMethod(int); method public void setSize(long); method public void setTime(long); @@ -66287,6 +66293,7 @@ package java.util.zip { method public java.io.InputStream getInputStream(java.util.zip.ZipEntry) throws java.io.IOException; method public java.lang.String getName(); method public int size(); + method public java.util.stream.Stream<? extends java.util.zip.ZipEntry> stream(); field public static final int CENATT = 36; // 0x24 field public static final int CENATX = 38; // 0x26 field public static final int CENCOM = 32; // 0x20 diff --git a/api/test-current.txt b/api/test-current.txt index 29de7a5af703..5301443f930b 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -62930,7 +62930,10 @@ package java.util.zip { method public java.lang.String getComment(); method public long getCompressedSize(); method public long getCrc(); + method public java.nio.file.attribute.FileTime getCreationTime(); method public byte[] getExtra(); + method public java.nio.file.attribute.FileTime getLastAccessTime(); + method public java.nio.file.attribute.FileTime getLastModifiedTime(); method public int getMethod(); method public java.lang.String getName(); method public long getSize(); @@ -62939,7 +62942,10 @@ package java.util.zip { method public void setComment(java.lang.String); method public void setCompressedSize(long); method public void setCrc(long); + method public java.util.zip.ZipEntry setCreationTime(java.nio.file.attribute.FileTime); method public void setExtra(byte[]); + method public java.util.zip.ZipEntry setLastAccessTime(java.nio.file.attribute.FileTime); + method public java.util.zip.ZipEntry setLastModifiedTime(java.nio.file.attribute.FileTime); method public void setMethod(int); method public void setSize(long); method public void setTime(long); @@ -63010,6 +63016,7 @@ package java.util.zip { method public java.io.InputStream getInputStream(java.util.zip.ZipEntry) throws java.io.IOException; method public java.lang.String getName(); method public int size(); + method public java.util.stream.Stream<? extends java.util.zip.ZipEntry> stream(); field public static final int CENATT = 36; // 0x24 field public static final int CENATX = 38; // 0x26 field public static final int CENCOM = 32; // 0x20 diff --git a/cmds/svc/src/com/android/commands/svc/UsbCommand.java b/cmds/svc/src/com/android/commands/svc/UsbCommand.java index a6ef25fc4479..4dcb05e4f85d 100644 --- a/cmds/svc/src/com/android/commands/svc/UsbCommand.java +++ b/cmds/svc/src/com/android/commands/svc/UsbCommand.java @@ -50,7 +50,7 @@ public class UsbCommand extends Svc.Command { IUsbManager usbMgr = IUsbManager.Stub.asInterface(ServiceManager.getService( Context.USB_SERVICE)); try { - usbMgr.setCurrentFunction((args.length >=3 ? args[2] : null)); + usbMgr.setCurrentFunction((args.length >=3 ? args[2] : null), false); } catch (RemoteException e) { System.err.println("Error communicating with UsbManager: " + e); } diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl index 6e4c9de1c8ef..00b0bffdd1f5 100644 --- a/core/java/android/hardware/usb/IUsbManager.aidl +++ b/core/java/android/hardware/usb/IUsbManager.aidl @@ -87,15 +87,13 @@ interface IUsbManager /* Returns true if the specified USB function is enabled. */ boolean isFunctionEnabled(String function); - /* Sets the current USB function. */ - void setCurrentFunction(String function); - - /* Sets whether USB data (for example, MTP exposed pictures) should be made - * available on the USB connection. Unlocking data should only be done with - * user involvement, since exposing pictures or other data could leak sensitive - * user information. + /* Sets the current USB function as well as whether USB data + * (for example, MTP exposed pictures) should be made available + * on the USB connection. Unlocking data should only be done with + * user involvement, since exposing pictures or other data could + * leak sensitive user information. */ - void setUsbDataUnlocked(boolean unlock); + void setCurrentFunction(String function, boolean usbDataUnlocked); /* Allow USB debugging from the attached host. If alwaysAllow is true, add the * the public key to list of host keys that the user has approved. diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java index f9a7d192b5bb..6341cbc79f3f 100644 --- a/core/java/android/hardware/usb/UsbManager.java +++ b/core/java/android/hardware/usb/UsbManager.java @@ -509,33 +509,23 @@ public class UsbManager { * {@link #USB_FUNCTION_MIDI}, {@link #USB_FUNCTION_MTP}, {@link #USB_FUNCTION_PTP}, * or {@link #USB_FUNCTION_RNDIS}. * </p><p> + * Also sets whether USB data (for example, MTP exposed pictures) should be made available + * on the USB connection when in device mode. Unlocking usb data should only be done with + * user involvement, since exposing pictures or other data could leak sensitive + * user information. + * </p><p> * Note: This function is asynchronous and may fail silently without applying * the requested changes. * </p> * * @param function name of the USB function, or null to restore the default function + * @param usbDataUnlocked whether user data is accessible * * {@hide} */ - public void setCurrentFunction(String function) { - try { - mService.setCurrentFunction(function); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Sets whether USB data (for example, MTP exposed pictures) should be made available - * on the USB connection when in device mode. Unlocking usb data should only be done with - * user involvement, since exposing pictures or other data could leak sensitive - * user information. - * - * {@hide} - */ - public void setUsbDataUnlocked(boolean unlocked) { + public void setCurrentFunction(String function, boolean usbDataUnlocked) { try { - mService.setUsbDataUnlocked(unlocked); + mService.setCurrentFunction(function, usbDataUnlocked); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/os/CountDownTimer.java b/core/java/android/os/CountDownTimer.java index 58acbcf5684b..c7bf0fd6ba49 100644 --- a/core/java/android/os/CountDownTimer.java +++ b/core/java/android/os/CountDownTimer.java @@ -125,19 +125,28 @@ public abstract class CountDownTimer { if (millisLeft <= 0) { onFinish(); - } else if (millisLeft < mCountdownInterval) { - // no tick, just delay until done - sendMessageDelayed(obtainMessage(MSG), millisLeft); } else { long lastTickStart = SystemClock.elapsedRealtime(); onTick(millisLeft); // take into account user's onTick taking time to execute - long delay = lastTickStart + mCountdownInterval - SystemClock.elapsedRealtime(); - - // special case: user's onTick took more than interval to - // complete, skip to next interval - while (delay < 0) delay += mCountdownInterval; + long lastTickDuration = SystemClock.elapsedRealtime() - lastTickStart; + long delay; + + if (millisLeft < mCountdownInterval) { + // just delay until done + delay = millisLeft - lastTickDuration; + + // special case: user's onTick took more than interval to + // complete, trigger onFinish without delay + if (delay < 0) delay = 0; + } else { + delay = mCountdownInterval - lastTickDuration; + + // special case: user's onTick took more than interval to + // complete, skip to next interval + while (delay < 0) delay += mCountdownInterval; + } sendMessageDelayed(obtainMessage(MSG), delay); } diff --git a/core/jni/android_os_HwBlob.h b/core/jni/android_os_HwBlob.h index 6bd82e98e911..09204880881b 100644 --- a/core/jni/android_os_HwBlob.h +++ b/core/jni/android_os_HwBlob.h @@ -20,6 +20,7 @@ #include <android-base/macros.h> #include <jni.h> #include <hidl/HidlSupport.h> +#include <hwbinder/Parcel.h> #include <utils/RefBase.h> #include <utils/Vector.h> diff --git a/core/jni/android_os_HwParcel.cpp b/core/jni/android_os_HwParcel.cpp index 1a67ceebac1c..a10d80746e46 100644 --- a/core/jni/android_os_HwParcel.cpp +++ b/core/jni/android_os_HwParcel.cpp @@ -26,6 +26,7 @@ #include <JNIHelp.h> #include <android_runtime/AndroidRuntime.h> +#include <hidl/HidlTransportSupport.h> #include <hidl/Status.h> #include <nativehelper/ScopedLocalRef.h> @@ -383,7 +384,7 @@ static void JHwParcel_native_writeStatus( hardware::Parcel *parcel = JHwParcel::GetNativeContext(env, thiz)->getParcel(); - status_t err = status.writeToParcel(parcel); + status_t err = ::android::hardware::writeToParcel(status, parcel); signalExceptionForError(env, err); } @@ -394,7 +395,7 @@ static void JHwParcel_native_verifySuccess(JNIEnv *env, jobject thiz) { JHwParcel::GetNativeContext(env, thiz)->getParcel(); Status status; - status_t err = status.readFromParcel(*parcel); + status_t err = ::android::hardware::readFromParcel(&status, *parcel); signalExceptionForError(env, err); } diff --git a/core/tests/coretests/apks/install_jni_lib/Android.mk b/core/tests/coretests/apks/install_jni_lib/Android.mk index 9e45d099b669..d7b38e844b5e 100644 --- a/core/tests/coretests/apks/install_jni_lib/Android.mk +++ b/core/tests/coretests/apks/install_jni_lib/Android.mk @@ -19,8 +19,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := \ com_android_frameworks_coretests_JNITest.cpp -LOCAL_SHARED_LIBRARIES := \ - libnativehelper +LOCAL_SDK_VERSION := 16 LOCAL_CFLAGS += -Wall -Werror diff --git a/core/tests/coretests/apks/install_jni_lib/com_android_frameworks_coretests_JNITest.cpp b/core/tests/coretests/apks/install_jni_lib/com_android_frameworks_coretests_JNITest.cpp index 8d9119275bc8..0cf3a84a3859 100644 --- a/core/tests/coretests/apks/install_jni_lib/com_android_frameworks_coretests_JNITest.cpp +++ b/core/tests/coretests/apks/install_jni_lib/com_android_frameworks_coretests_JNITest.cpp @@ -14,41 +14,23 @@ * limitations under the License. */ -#include "nativehelper/JNIHelp.h" +#include <jni.h> -namespace android { - -static jint checkFunction(JNIEnv*, jclass) { +extern "C" JNIEXPORT +jint JNICALL Java_com_android_frameworks_coretests_JNITests_checkFunction(JNIEnv*, jclass) { return 1; } -static const JNINativeMethod sMethods[] = { - /* name, signature, funcPtr */ - { "checkFunction", "()I", (void*) checkFunction }, -}; - -int register_com_android_frameworks_coretests_JNITests(JNIEnv* env) { - return jniRegisterNativeMethods(env, "com/android/frameworks/coretests/JNITests", sMethods, - NELEM(sMethods)); -} - -} - /* * JNI Initialization */ jint JNI_OnLoad(JavaVM *jvm, void */* reserved */) { JNIEnv *e; - int status; // Check JNI version if (jvm->GetEnv((void **) &e, JNI_VERSION_1_6)) { return JNI_ERR; } - if ((status = android::register_com_android_frameworks_coretests_JNITests(e)) < 0) { - return JNI_ERR; - } - return JNI_VERSION_1_6; } diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 3c1c0bceba58..0d9ede063c79 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -20,6 +20,7 @@ #include "CanvasContext.h" #include "EglManager.h" #include "RenderProxy.h" +#include "utils/FatVector.h" #include <gui/DisplayEventReceiver.h> #include <gui/ISurfaceComposer.h> @@ -282,10 +283,18 @@ bool RenderThread::threadLoop() { "RenderThread Looper POLL_ERROR!"); nsecs_t nextWakeup; - // Process our queue, if we have anything - while (RenderTask* task = nextTask(&nextWakeup)) { - task->run(); - // task may have deleted itself, do not reference it again + { + FatVector<RenderTask*, 10> workQueue; + // Process our queue, if we have anything. By first acquiring + // all the pending events then processing them we avoid vsync + // starvation if more tasks are queued while we are processing tasks. + while (RenderTask* task = nextTask(&nextWakeup)) { + workQueue.push_back(task); + } + for (auto task : workQueue) { + task->run(); + // task may have deleted itself, do not reference it again + } } if (nextWakeup == LLONG_MAX) { timeoutMillis = -1; diff --git a/libs/hwui/tests/microbench/FrameBuilderBench.cpp b/libs/hwui/tests/microbench/FrameBuilderBench.cpp index 84ef9c2575f5..362890b52b61 100644 --- a/libs/hwui/tests/microbench/FrameBuilderBench.cpp +++ b/libs/hwui/tests/microbench/FrameBuilderBench.cpp @@ -113,7 +113,7 @@ static auto SCENES = { void BM_FrameBuilder_defer_scene(benchmark::State& state) { TestUtils::runOnRenderThread([&state](RenderThread& thread) { - const char* sceneName = *(SCENES.begin() + state.range_x()); + const char* sceneName = *(SCENES.begin() + state.range(0)); state.SetLabel(sceneName); auto node = getSyncedSceneNode(sceneName); while (state.KeepRunning()) { @@ -129,7 +129,7 @@ BENCHMARK(BM_FrameBuilder_defer_scene)->DenseRange(0, SCENES.size() - 1); void BM_FrameBuilder_deferAndRender_scene(benchmark::State& state) { TestUtils::runOnRenderThread([&state](RenderThread& thread) { - const char* sceneName = *(SCENES.begin() + state.range_x()); + const char* sceneName = *(SCENES.begin() + state.range(0)); state.SetLabel(sceneName); auto node = getSyncedSceneNode(sceneName); diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java index 9608daad70e9..24ede164fbdd 100755 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java @@ -116,6 +116,10 @@ public final class A2dpProfile implements LocalBluetoothProfile { List<BluetoothDevice> sinks = getConnectedDevices(); if (sinks != null) { for (BluetoothDevice sink : sinks) { + if (sink.equals(device)) { + Log.w(TAG, "Connecting to device " + device + " : disconnect skipped"); + continue; + } mService.disconnect(sink); } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index b59f4686998e..0f54d23ce75b 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -6351,13 +6351,18 @@ public final class ActivityManagerService extends ActivityManagerNative removeLruProcessLocked(app); if (mBackupTarget != null && mBackupTarget.app.pid == pid) { Slog.w(TAG, "Unattached app died before backup, skipping"); - try { - IBackupManager bm = IBackupManager.Stub.asInterface( - ServiceManager.getService(Context.BACKUP_SERVICE)); - bm.agentDisconnected(app.info.packageName); - } catch (RemoteException e) { - // Can't happen; the backup manager is local - } + mHandler.post(new Runnable() { + @Override + public void run(){ + try { + IBackupManager bm = IBackupManager.Stub.asInterface( + ServiceManager.getService(Context.BACKUP_SERVICE)); + bm.agentDisconnected(app.info.packageName); + } catch (RemoteException e) { + // Can't happen; the backup manager is local + } + } + }); } if (isPendingBroadcastProcessLocked(pid)) { Slog.w(TAG, "Unattached app died before broadcast acknowledged, skipping"); @@ -16766,13 +16771,18 @@ public final class ActivityManagerService extends ActivityManagerNative if (mBackupTarget != null && app.pid == mBackupTarget.app.pid) { if (DEBUG_BACKUP || DEBUG_CLEANUP) Slog.d(TAG_CLEANUP, "App " + mBackupTarget.appInfo + " died during backup"); - try { - IBackupManager bm = IBackupManager.Stub.asInterface( - ServiceManager.getService(Context.BACKUP_SERVICE)); - bm.agentDisconnected(app.info.packageName); - } catch (RemoteException e) { - // can't happen; backup manager is local - } + mHandler.post(new Runnable() { + @Override + public void run(){ + try { + IBackupManager bm = IBackupManager.Stub.asInterface( + ServiceManager.getService(Context.BACKUP_SERVICE)); + bm.agentDisconnected(app.info.packageName); + } catch (RemoteException e) { + // can't happen; backup manager is local + } + } + }); } for (int i = mPendingProcessChanges.size() - 1; i >= 0; i--) { diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java index e888faaac075..cec4141a41f6 100644 --- a/services/core/java/com/android/server/connectivity/Tethering.java +++ b/services/core/java/com/android/server/connectivity/Tethering.java @@ -900,7 +900,7 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering } } else { mUsbTetherRequested = true; - usbManager.setCurrentFunction(UsbManager.USB_FUNCTION_RNDIS); + usbManager.setCurrentFunction(UsbManager.USB_FUNCTION_RNDIS, false); } } else { final long ident = Binder.clearCallingIdentity(); @@ -910,7 +910,7 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering Binder.restoreCallingIdentity(ident); } if (mRndisEnabled) { - usbManager.setCurrentFunction(null); + usbManager.setCurrentFunction(null, false); } mUsbTetherRequested = false; } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index dfd6dfe1c775..99a6979d3436 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -1410,7 +1410,6 @@ final class Settings { VersionInfo ver = mVersion.get(volumeUuid); if (ver == null) { ver = new VersionInfo(); - ver.forceCurrent(); mVersion.put(volumeUuid, ver); } return ver; @@ -2795,8 +2794,8 @@ final class Settings { "No settings file; creating initial state"); // It's enough to just touch version details to create them // with default values - findOrCreateVersion(StorageManager.UUID_PRIVATE_INTERNAL); - findOrCreateVersion(StorageManager.UUID_PRIMARY_PHYSICAL); + findOrCreateVersion(StorageManager.UUID_PRIVATE_INTERNAL).forceCurrent(); + findOrCreateVersion(StorageManager.UUID_PRIMARY_PHYSICAL).forceCurrent(); return false; } str = new FileInputStream(mSettingsFilename); diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java index df9242dc0aa1..43b8fa51a80e 100644 --- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java @@ -108,9 +108,8 @@ public class UsbDeviceManager { private static final int MSG_SYSTEM_READY = 3; private static final int MSG_BOOT_COMPLETED = 4; private static final int MSG_USER_SWITCHED = 5; - private static final int MSG_SET_USB_DATA_UNLOCKED = 6; - private static final int MSG_UPDATE_USER_RESTRICTIONS = 7; - private static final int MSG_UPDATE_HOST_STATE = 8; + private static final int MSG_UPDATE_USER_RESTRICTIONS = 6; + private static final int MSG_UPDATE_HOST_STATE = 7; private static final int AUDIO_MODE_SOURCE = 1; @@ -287,7 +286,7 @@ public class UsbDeviceManager { if (functions != null) { mAccessoryModeRequestTime = SystemClock.elapsedRealtime(); - setCurrentFunctions(functions); + setCurrentFunctions(functions, false); } } @@ -335,14 +334,22 @@ public class UsbDeviceManager { // Restore default functions. mCurrentFunctions = SystemProperties.get(USB_CONFIG_PROPERTY, UsbManager.USB_FUNCTION_NONE); - if (UsbManager.USB_FUNCTION_NONE.equals(mCurrentFunctions)) { - mCurrentFunctions = UsbManager.USB_FUNCTION_MTP; - } mCurrentFunctionsApplied = mCurrentFunctions.equals( SystemProperties.get(USB_STATE_PROPERTY)); mAdbEnabled = UsbManager.containsFunction(getDefaultFunctions(), UsbManager.USB_FUNCTION_ADB); - setEnabledFunctions(null, false); + + /** + * Remove MTP from persistent config, to bring usb to a good state + * after fixes to b/31814300. This block can be removed after the update + */ + String persisted = SystemProperties.get(USB_PERSISTENT_CONFIG_PROPERTY); + if (UsbManager.containsFunction(persisted, UsbManager.USB_FUNCTION_MTP)) { + SystemProperties.set(USB_PERSISTENT_CONFIG_PROPERTY, + UsbManager.removeFunction(persisted, UsbManager.USB_FUNCTION_MTP)); + } + + setEnabledFunctions(null, false, false); String state = FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim(); updateState(state); @@ -374,6 +381,14 @@ public class UsbDeviceManager { sendMessage(m); } + public void sendMessage(int what, Object arg, boolean arg1) { + removeMessages(what); + Message m = Message.obtain(this, what); + m.obj = arg; + m.arg1 = (arg1 ? 1 : 0); + sendMessage(m); + } + public void updateState(String state) { int connected, configured; @@ -427,29 +442,24 @@ public class UsbDeviceManager { return waitForState(config); } - private void setUsbDataUnlocked(boolean enable) { - if (DEBUG) Slog.d(TAG, "setUsbDataUnlocked: " + enable); - mUsbDataUnlocked = enable; - updateUsbNotification(); - updateUsbStateBroadcastIfNeeded(); - setEnabledFunctions(mCurrentFunctions, true); - } - private void setAdbEnabled(boolean enable) { if (DEBUG) Slog.d(TAG, "setAdbEnabled: " + enable); if (enable != mAdbEnabled) { mAdbEnabled = enable; + String oldFunctions = mCurrentFunctions; + + // Persist the adb setting + String newFunction = applyAdbFunction(SystemProperties.get( + USB_PERSISTENT_CONFIG_PROPERTY, UsbManager.USB_FUNCTION_NONE)); + SystemProperties.set(USB_PERSISTENT_CONFIG_PROPERTY, newFunction); - // Due to the persist.sys.usb.config property trigger, changing adb state requires - // persisting default function - String oldFunctions = getDefaultFunctions(); - String newFunctions = applyAdbFunction(oldFunctions); - if (!oldFunctions.equals(newFunctions)) { - SystemProperties.set(USB_PERSISTENT_CONFIG_PROPERTY, newFunctions); + // Remove mtp from the config if file transfer is not enabled + if (oldFunctions.equals(UsbManager.USB_FUNCTION_MTP) && + !mUsbDataUnlocked && enable) { + oldFunctions = UsbManager.USB_FUNCTION_NONE; } - // After persisting them use the lock-down aware function set - setEnabledFunctions(mCurrentFunctions, false); + setEnabledFunctions(oldFunctions, true, mUsbDataUnlocked); updateAdbNotification(); } @@ -461,10 +471,17 @@ public class UsbDeviceManager { /** * Evaluates USB function policies and applies the change accordingly. */ - private void setEnabledFunctions(String functions, boolean forceRestart) { + private void setEnabledFunctions(String functions, boolean forceRestart, + boolean usbDataUnlocked) { if (DEBUG) Slog.d(TAG, "setEnabledFunctions functions=" + functions + ", " + "forceRestart=" + forceRestart); + if (usbDataUnlocked != mUsbDataUnlocked) { + mUsbDataUnlocked = usbDataUnlocked; + updateUsbNotification(); + forceRestart = true; + } + // Try to set the enabled functions. final String oldFunctions = mCurrentFunctions; final boolean oldFunctionsApplied = mCurrentFunctionsApplied; @@ -501,7 +518,8 @@ public class UsbDeviceManager { } private boolean trySetEnabledFunctions(String functions, boolean forceRestart) { - if (functions == null) { + if (functions == null || applyAdbFunction(functions) + .equals(UsbManager.USB_FUNCTION_NONE)) { functions = getDefaultFunctions(); } functions = applyAdbFunction(functions); @@ -566,7 +584,7 @@ public class UsbDeviceManager { // make sure accessory mode is off // and restore default functions Slog.d(TAG, "exited USB accessory mode"); - setEnabledFunctions(null, false); + setEnabledFunctions(null, false, false); if (mCurrentAccessory != null) { if (mBootCompleted) { @@ -583,10 +601,6 @@ public class UsbDeviceManager { if (mBroadcastedIntent == null) { for (String key : keySet) { if (intent.getBooleanExtra(key, false)) { - // MTP function is enabled by default. - if (UsbManager.USB_FUNCTION_MTP.equals(key)) { - continue; - } return true; } } @@ -699,10 +713,7 @@ public class UsbDeviceManager { case MSG_UPDATE_STATE: mConnected = (msg.arg1 == 1); mConfigured = (msg.arg2 == 1); - if (!mConnected) { - // When a disconnect occurs, relock access to sensitive user data - mUsbDataUnlocked = false; - } + updateUsbNotification(); updateAdbNotification(); if (UsbManager.containsFunction(mCurrentFunctions, @@ -710,7 +721,7 @@ public class UsbDeviceManager { updateCurrentAccessory(); } else if (!mConnected) { // restore defaults when USB is disconnected - setEnabledFunctions(null, false); + setEnabledFunctions(null, false, false); } if (mBootCompleted) { updateUsbStateBroadcastIfNeeded(); @@ -730,13 +741,10 @@ public class UsbDeviceManager { break; case MSG_SET_CURRENT_FUNCTIONS: String functions = (String)msg.obj; - setEnabledFunctions(functions, false); + setEnabledFunctions(functions, false, msg.arg1 == 1); break; case MSG_UPDATE_USER_RESTRICTIONS: - setEnabledFunctions(mCurrentFunctions, false); - break; - case MSG_SET_USB_DATA_UNLOCKED: - setUsbDataUnlocked(msg.arg1 == 1); + setEnabledFunctions(mCurrentFunctions, false, mUsbDataUnlocked); break; case MSG_SYSTEM_READY: updateUsbNotification(); @@ -764,8 +772,7 @@ public class UsbDeviceManager { Slog.v(TAG, "Current user switched to " + mCurrentUser + "; resetting USB host stack for MTP or PTP"); // avoid leaking sensitive data from previous user - mUsbDataUnlocked = false; - setEnabledFunctions(mCurrentFunctions, true); + setEnabledFunctions(mCurrentFunctions, true, false); } mCurrentUser = msg.arg1; } @@ -944,14 +951,10 @@ public class UsbDeviceManager { return UsbManager.containsFunction(SystemProperties.get(USB_CONFIG_PROPERTY), function); } - public void setCurrentFunctions(String functions) { - if (DEBUG) Slog.d(TAG, "setCurrentFunctions(" + functions + ")"); - mHandler.sendMessage(MSG_SET_CURRENT_FUNCTIONS, functions); - } - - public void setUsbDataUnlocked(boolean unlocked) { - if (DEBUG) Slog.d(TAG, "setUsbDataUnlocked(" + unlocked + ")"); - mHandler.sendMessage(MSG_SET_USB_DATA_UNLOCKED, unlocked); + public void setCurrentFunctions(String functions, boolean usbDataUnlocked) { + if (DEBUG) Slog.d(TAG, "setCurrentFunctions(" + functions + ", " + + usbDataUnlocked + ")"); + mHandler.sendMessage(MSG_SET_CURRENT_FUNCTIONS, functions, usbDataUnlocked); } private void readOemUsbOverrideConfig() { diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java index d6dbe90584f5..daccc00ad80c 100644 --- a/services/usb/java/com/android/server/usb/UsbService.java +++ b/services/usb/java/com/android/server/usb/UsbService.java @@ -287,7 +287,7 @@ public class UsbService extends IUsbManager.Stub { } @Override - public void setCurrentFunction(String function) { + public void setCurrentFunction(String function, boolean usbDataUnlocked) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); if (!isSupportedCurrentFunction(function)) { @@ -297,7 +297,7 @@ public class UsbService extends IUsbManager.Stub { } if (mDeviceManager != null) { - mDeviceManager.setCurrentFunctions(function); + mDeviceManager.setCurrentFunctions(function, usbDataUnlocked); } else { throw new IllegalStateException("USB device mode not supported"); } @@ -320,12 +320,6 @@ public class UsbService extends IUsbManager.Stub { } @Override - public void setUsbDataUnlocked(boolean unlocked) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); - mDeviceManager.setUsbDataUnlocked(unlocked); - } - - @Override public void allowUsbDebugging(boolean alwaysAllow, String publicKey) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); mDeviceManager.allowUsbDebugging(alwaysAllow, publicKey); diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java index 66d704b2fe81..f177a419e4c6 100644 --- a/telecomm/java/android/telecom/ConnectionService.java +++ b/telecomm/java/android/telecom/ConnectionService.java @@ -515,9 +515,12 @@ public abstract class ConnectionService extends Service { final boolean isUnknown = args.argi2 == 1; if (!mAreAccountsInitialized) { Log.d(this, "Enqueueing pre-init request %s", id); - mPreInitializationConnectionRequests.add(new Runnable() { + mPreInitializationConnectionRequests.add( + new android.telecom.Logging.Runnable( + SESSION_HANDLER + SESSION_CREATE_CONN + ".pICR", + null /*lock*/) { @Override - public void run() { + public void loggedRun() { createConnection( connectionManagerPhoneAccount, id, @@ -525,7 +528,7 @@ public abstract class ConnectionService extends Service { isIncoming, isUnknown); } - }); + }.prepare()); } else { createConnection( connectionManagerPhoneAccount, @@ -1378,9 +1381,9 @@ public abstract class ConnectionService extends Service { public void onResult( final List<ComponentName> componentNames, final List<IBinder> services) { - mHandler.post(new Runnable() { + mHandler.post(new android.telecom.Logging.Runnable("oAA.qRCS.oR", null /*lock*/) { @Override - public void run() { + public void loggedRun() { for (int i = 0; i < componentNames.size() && i < services.size(); i++) { mRemoteConnectionManager.addConnectionService( componentNames.get(i), @@ -1389,17 +1392,17 @@ public abstract class ConnectionService extends Service { onAccountsInitialized(); Log.d(this, "remote connection services found: " + services); } - }); + }.prepare()); } @Override public void onError() { - mHandler.post(new Runnable() { + mHandler.post(new android.telecom.Logging.Runnable("oAA.qRCS.oE", null /*lock*/) { @Override - public void run() { + public void loggedRun() { mAreAccountsInitialized = true; } - }); + }.prepare()); } }); } diff --git a/telecomm/java/android/telecom/Log.java b/telecomm/java/android/telecom/Log.java index 446bbbb69996..ced6627e8d34 100644 --- a/telecomm/java/android/telecom/Log.java +++ b/telecomm/java/android/telecom/Log.java @@ -48,13 +48,13 @@ public class Log { // Generic tag for all Telecom logging @VisibleForTesting public static String TAG = "TelecomFramework"; + public static boolean DEBUG = isLoggable(android.util.Log.DEBUG); + public static boolean INFO = isLoggable(android.util.Log.INFO); + public static boolean VERBOSE = isLoggable(android.util.Log.VERBOSE); + public static boolean WARN = isLoggable(android.util.Log.WARN); + public static boolean ERROR = isLoggable(android.util.Log.ERROR); private static final boolean FORCE_LOGGING = false; /* STOP SHIP if true */ - public static final boolean DEBUG = isLoggable(android.util.Log.DEBUG); - public static final boolean INFO = isLoggable(android.util.Log.INFO); - public static final boolean VERBOSE = isLoggable(android.util.Log.VERBOSE); - public static final boolean WARN = isLoggable(android.util.Log.WARN); - public static final boolean ERROR = isLoggable(android.util.Log.ERROR); // Used to synchronize singleton logging lazy initialization private static final Object sSingletonSync = new Object(); @@ -340,6 +340,11 @@ public class Log { public static void setTag(String tag) { TAG = tag; + DEBUG = isLoggable(android.util.Log.DEBUG); + INFO = isLoggable(android.util.Log.INFO); + VERBOSE = isLoggable(android.util.Log.VERBOSE); + WARN = isLoggable(android.util.Log.WARN); + ERROR = isLoggable(android.util.Log.ERROR); } /** diff --git a/telecomm/java/android/telecom/Logging/Runnable.java b/telecomm/java/android/telecom/Logging/Runnable.java index 56c52bfde3b3..6e810538cc62 100644 --- a/telecomm/java/android/telecom/Logging/Runnable.java +++ b/telecomm/java/android/telecom/Logging/Runnable.java @@ -27,7 +27,7 @@ public abstract class Runnable { private Session mSubsession; private final String mSubsessionName; - private final Object mLock = new Object(); + private final Object mLock; private final java.lang.Runnable mRunnable = new java.lang.Runnable() { @Override public void run() { @@ -45,7 +45,18 @@ public abstract class Runnable { } }; - public Runnable(String subsessionName) { + /** + * Creates a new Telecom Runnable that incorporates Session Logging into it. Useful for carrying + * Logging Sessions through different threads as well as through handlers. + * @param subsessionName The name that will be used in the Logs to mark this Session + * @param lock The synchronization lock that will be used to lock loggedRun(). + */ + public Runnable(String subsessionName, Object lock) { + if (lock == null) { + mLock = new Object(); + } else { + mLock = lock; + } mSubsessionName = subsessionName; } @@ -85,4 +96,4 @@ public abstract class Runnable { */ abstract public void loggedRun(); -} +}
\ No newline at end of file diff --git a/telecomm/java/android/telecom/Logging/Session.java b/telecomm/java/android/telecom/Logging/Session.java index 3a7b8c09203a..c45bd6b04145 100644 --- a/telecomm/java/android/telecom/Logging/Session.java +++ b/telecomm/java/android/telecom/Logging/Session.java @@ -19,6 +19,7 @@ package android.telecom.Logging; import android.annotation.NonNull; import android.os.Parcel; import android.os.Parcelable; +import android.telecom.Log; import android.text.TextUtils; import com.android.internal.annotations.VisibleForTesting; @@ -26,20 +27,23 @@ import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayList; /** - * The session that stores information about a thread's point of entry into the Telecom code that - * persists until the thread exits Telecom. + * Stores information about a thread's point of entry into that should persist until that thread + * exits. * @hide */ public class Session { public static final String START_SESSION = "START_SESSION"; + public static final String START_EXTERNAL_SESSION = "START_EXTERNAL_SESSION"; public static final String CREATE_SUBSESSION = "CREATE_SUBSESSION"; public static final String CONTINUE_SUBSESSION = "CONTINUE_SUBSESSION"; public static final String END_SUBSESSION = "END_SUBSESSION"; public static final String END_SESSION = "END_SESSION"; public static final String SUBSESSION_SEPARATION_CHAR = "->"; + public static final String SESSION_SEPARATION_CHAR_CHILD = "_"; public static final String EXTERNAL_INDICATOR = "E-"; + public static final String TRUNCATE_STRING = "..."; /** * Initial value of mExecutionEndTimeMs and the final value of {@link #getLocalExecutionTime()} @@ -49,15 +53,19 @@ public class Session { public static class Info implements Parcelable { public final String sessionId; - public final String shortMethodName; + public final String methodPath; - private Info(String id, String methodName) { + private Info(String id, String path) { sessionId = id; - shortMethodName = methodName; + methodPath = path; } public static Info getInfo (Session s) { - return new Info(s.getFullSessionId(), s.getShortMethodName()); + // Create Info based on the truncated method path if the session is external, so we do + // not get multiple stacking external sessions (unless we have DEBUG level logging or + // lower). + return new Info(s.getFullSessionId(), s.getFullMethodPath( + !Log.DEBUG && s.isSessionExternal())); } /** Responsible for creating Info objects for deserialized Parcels. */ @@ -86,7 +94,7 @@ public class Session { @Override public void writeToParcel(Parcel destination, int flags) { destination.writeString(sessionId); - destination.writeString(shortMethodName); + destination.writeString(methodPath); } } @@ -226,7 +234,15 @@ public class Session { if (parentSession == null) { return mSessionId; } else { - return parentSession.getFullSessionId() + "_" + mSessionId; + if (Log.VERBOSE) { + return parentSession.getFullSessionId() + + // Append "_X" to subsession to show subsession designation. + SESSION_SEPARATION_CHAR_CHILD + mSessionId; + } else { + // Only worry about the base ID at the top of the tree. + return parentSession.getFullSessionId(); + } + } } @@ -259,16 +275,18 @@ public class Session { } // Recursively concatenate mShortMethodName with the parent Sessions to create full method - // path. Caches this string so that multiple calls for the path will be quick. - public String getFullMethodPath() { + // path. if truncatePath is set to true, all other external sessions (except for the most + // recent) will be truncated to "..." + public String getFullMethodPath(boolean truncatePath) { StringBuilder sb = new StringBuilder(); - getFullMethodPath(sb); + getFullMethodPath(sb, truncatePath); return sb.toString(); } - private synchronized void getFullMethodPath(StringBuilder sb) { - // Don't calculate if we have already figured it out! - if (!TextUtils.isEmpty(mFullMethodPathCache)) { + private synchronized void getFullMethodPath(StringBuilder sb, boolean truncatePath) { + // Return cached value for method path. When returning the truncated path, recalculate the + // full path without using the cached value. + if (!TextUtils.isEmpty(mFullMethodPathCache) && !truncatePath) { sb.append(mFullMethodPathCache); return; } @@ -278,25 +296,37 @@ public class Session { // Check to see if the session has been renamed yet. If it has not, then the session // has not been continued. isSessionStarted = !mShortMethodName.equals(parentSession.mShortMethodName); - parentSession.getFullMethodPath(sb); + parentSession.getFullMethodPath(sb, truncatePath); sb.append(SUBSESSION_SEPARATION_CHAR); } // Encapsulate the external session's method name so it is obvious what part of the session - // is external. + // is external or truncate it if we do not want the entire history. if (isExternal()) { - sb.append("("); - sb.append(mShortMethodName); - sb.append(")"); + if (truncatePath) { + sb.append(TRUNCATE_STRING); + } else { + sb.append("("); + sb.append(mShortMethodName); + sb.append(")"); + } } else { sb.append(mShortMethodName); } - - if(isSessionStarted) { + // If we are returning the truncated path, do not save that path as the full path. + if (isSessionStarted && !truncatePath) { // Cache this value so that we do not have to do this work next time! // We do not cache the value if the session being evaluated hasn't been continued yet. mFullMethodPathCache = sb.toString(); } } + // Recursively move to the top of the tree to see if the parent session is external. + private boolean isSessionExternal() { + if (getParentSession() == null) { + return isExternal(); + } else { + return getParentSession().isSessionExternal(); + } + } @Override public int hashCode() { @@ -350,7 +380,7 @@ public class Session { return mParentSession.toString(); } else { StringBuilder methodName = new StringBuilder(); - methodName.append(getFullMethodPath()); + methodName.append(getFullMethodPath(false /*truncatePath*/)); if (mOwnerInfo != null && !mOwnerInfo.isEmpty()) { methodName.append("(InCall package: "); methodName.append(mOwnerInfo); diff --git a/telecomm/java/android/telecom/Logging/SessionManager.java b/telecomm/java/android/telecom/Logging/SessionManager.java index 8ced7f8181c5..949f7b7a89ae 100644 --- a/telecomm/java/android/telecom/Logging/SessionManager.java +++ b/telecomm/java/android/telecom/Logging/SessionManager.java @@ -177,8 +177,9 @@ public class SessionManager { } // Create Session from Info and add to the sessionMapper under this ID. + Log.d(LOGGING_TAG, Session.START_EXTERNAL_SESSION); Session externalSession = new Session(Session.EXTERNAL_INDICATOR + sessionInfo.sessionId, - sessionInfo.shortMethodName, System.currentTimeMillis(), + sessionInfo.methodPath, System.currentTimeMillis(), false /*isStartedFromActiveSession*/, null); externalSession.setIsExternal(true); // Mark the external session as already completed, since we have no way of knowing when @@ -190,8 +191,6 @@ public class SessionManager { // Create a subsession from this external Session parent node Session childSession = createSubsession(); continueSession(childSession, shortMethodName); - - Log.d(LOGGING_TAG, Session.START_SESSION); } /** diff --git a/tools/preload2/Android.mk b/tools/preload2/Android.mk index ce877b3696c2..09d95ffe3506 100644 --- a/tools/preload2/Android.mk +++ b/tools/preload2/Android.mk @@ -5,7 +5,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-java-files-under,src) # To connect to devices (and take hprof dumps). -LOCAL_STATIC_JAVA_LIBRARIES := ddmlib-prebuilt +LOCAL_STATIC_JAVA_LIBRARIES := ddmlib-prebuilt tools-common-prebuilt # To process hprof dumps. LOCAL_STATIC_JAVA_LIBRARIES += perflib-prebuilt trove-prebuilt guavalib diff --git a/wifi/java/android/net/wifi/EAPConstants.java b/wifi/java/android/net/wifi/EAPConstants.java new file mode 100644 index 000000000000..b5f7c946ff05 --- /dev/null +++ b/wifi/java/android/net/wifi/EAPConstants.java @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2016, 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 android.net.wifi; + +/** + * Utility class containing EAP (Extensible Authentication Protocol) Related constants. + * + * @hide + */ +public final class EAPConstants { + // Constant definition for EAP types. Refer to + // http://www.iana.org/assignments/eap-numbers/eap-numbers.xhtml for more info. + public static final int EAP_MD5 = 4; + public static final int EAP_OTP = 5; + public static final int EAP_RSA = 9; + public static final int EAP_KEA = 11; + public static final int EAP_KEA_VALIDATE = 12; + public static final int EAP_TLS = 13; + public static final int EAP_LEAP = 17; + public static final int EAP_SIM = 18; + public static final int EAP_TTLS = 21; + public static final int EAP_AKA = 23; + public static final int EAP_3Com = 24; + public static final int EAP_MSCHAPv2 = 26; + public static final int EAP_PEAP = 29; + public static final int EAP_POTP = 32; + public static final int EAP_ActiontecWireless = 35; + public static final int EAP_HTTPDigest = 38; + public static final int EAP_SPEKE = 41; + public static final int EAP_MOBAC = 42; + public static final int EAP_FAST = 43; + public static final int EAP_ZLXEAP = 44; + public static final int EAP_Link = 45; + public static final int EAP_PAX = 46; + public static final int EAP_PSK = 47; + public static final int EAP_SAKE = 48; + public static final int EAP_IKEv2 = 49; + public static final int EAP_AKA_PRIME = 50; + public static final int EAP_GPSK = 51; + public static final int EAP_PWD = 52; + public static final int EAP_EKE = 53; + public static final int EAP_TEAP = 55; +} diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java index 465addfc1469..da8713555889 100644 --- a/wifi/java/android/net/wifi/ScanResult.java +++ b/wifi/java/android/net/wifi/ScanResult.java @@ -66,6 +66,93 @@ public class ScanResult implements Parcelable { * supported by the access point. */ public String capabilities; + + /** + * @hide + * No security protocol. + */ + public static final int PROTOCOL_NONE = 0; + /** + * @hide + * Security protocol type: WPA version 1. + */ + public static final int PROTOCOL_WPA = 1; + /** + * @hide + * Security protocol type: WPA version 2, also called RSN. + */ + public static final int PROTOCOL_WPA2 = 2; + /** + * @hide + * Security protocol type: + * OSU Server-only authenticated layer 2 Encryption Network. + * Used for Hotspot 2.0. + */ + public static final int PROTOCOL_OSEN = 3; + + /** + * @hide + * No security key management scheme. + */ + public static final int KEY_MGMT_NONE = 0; + /** + * @hide + * Security key management scheme: PSK. + */ + public static final int KEY_MGMT_PSK = 1; + /** + * @hide + * Security key management scheme: EAP. + */ + public static final int KEY_MGMT_EAP = 2; + /** + * @hide + * Security key management scheme: FT_PSK. + */ + public static final int KEY_MGMT_FT_PSK = 3; + /** + * @hide + * Security key management scheme: FT_EAP. + */ + public static final int KEY_MGMT_FT_EAP = 4; + /** + * @hide + * Security key management scheme: PSK_SHA256 + */ + public static final int KEY_MGMT_PSK_SHA256 = 5; + /** + * @hide + * Security key management scheme: EAP_SHA256. + */ + public static final int KEY_MGMT_EAP_SHA256 = 6; + /** + * @hide + * Security key management scheme: OSEN. + * Used for Hotspot 2.0. + */ + public static final int KEY_MGMT_OSEN = 7; + + /** + * @hide + * No cipher suite. + */ + public static final int CIPHER_NONE = 0; + /** + * @hide + * No group addressed, only used for group data cipher. + */ + public static final int CIPHER_NO_GROUP_ADDRESSED = 1; + /** + * @hide + * Cipher suite: TKIP + */ + public static final int CIPHER_TKIP = 2; + /** + * @hide + * Cipher suite: CCMP + */ + public static final int CIPHER_CCMP = 3; + /** * The detected signal level in dBm, also known as the RSSI. * diff --git a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java index 18aae534d098..a62a0fb582f1 100644 --- a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java +++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java @@ -61,6 +61,21 @@ public final class PasspointConfiguration implements Parcelable { credential.equals(that.credential)); } + /** + * Validate the configuration data. + * + * @return true on success or false on failure + */ + public boolean validate() { + if (homeSp == null || !homeSp.validate()) { + return false; + } + if (credential == null || !credential.validate()) { + return false; + } + return true; + } + public static final Creator<PasspointConfiguration> CREATOR = new Creator<PasspointConfiguration>() { @Override diff --git a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java index 92dbd8afb2d3..57e65eb7a190 100644 --- a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java +++ b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java @@ -16,15 +16,21 @@ package android.net.wifi.hotspot2.pps; +import android.net.wifi.EAPConstants; import android.net.wifi.ParcelUtil; import android.os.Parcelable; import android.os.Parcel; import android.text.TextUtils; +import android.util.Log; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; /** * Class representing Credential subtree in the PerProviderSubscription (PPS) @@ -40,6 +46,14 @@ import java.util.Arrays; * @hide */ public final class Credential implements Parcelable { + private static final String TAG = "Credential"; + + /** + * Max string length for realm. Refer to Credential/Realm node in Hotspot 2.0 Release 2 + * Technical Specification Section 9.1 for more info. + */ + private static final int MAX_REALM_LENGTH = 253; + /** * The realm associated with this credential. It will be used to determine * if this credential can be used to authenticate with a given hotspot by @@ -53,6 +67,26 @@ public final class Credential implements Parcelable { */ public static final class UserCredential implements Parcelable { /** + * Maximum string length for username. Refer to Credential/UsernamePassword/Username + * node in Hotspot 2.0 Release 2 Technical Specification Section 9.1 for more info. + */ + private static final int MAX_USERNAME_LENGTH = 63; + + /** + * Maximum string length for password. Refer to Credential/UsernamePassword/Password + * in Hotspot 2.0 Release 2 Technical Specification Section 9.1 for more info. + */ + private static final int MAX_PASSWORD_LENGTH = 255; + + /** + * Supported Non-EAP inner methods. Refer to + * Credential/UsernamePassword/EAPMethod/InnerEAPType in Hotspot 2.0 Release 2 Technical + * Specification Section 9.1 for more info. + */ + private static final Set<String> SUPPORTED_AUTH = + new HashSet<String>(Arrays.asList("PAP", "CHAP", "MS-CHAP", "MS-CHAP-V2")); + + /** * Username of the credential. */ public String username = null; @@ -104,6 +138,44 @@ public final class Credential implements Parcelable { TextUtils.equals(nonEapInnerMethod, that.nonEapInnerMethod); } + /** + * Validate the configuration data. + * + * @return true on success or false on failure + */ + public boolean validate() { + if (TextUtils.isEmpty(username)) { + Log.d(TAG, "Missing username"); + return false; + } + if (username.length() > MAX_USERNAME_LENGTH) { + Log.d(TAG, "username exceeding maximum length: " + username.length()); + return false; + } + + if (TextUtils.isEmpty(password)) { + Log.d(TAG, "Missing password"); + return false; + } + if (password.length() > MAX_PASSWORD_LENGTH) { + Log.d(TAG, "password exceeding maximum length: " + password.length()); + return false; + } + + // Only supports EAP-TTLS for user credential. + if (eapType != EAPConstants.EAP_TTLS) { + Log.d(TAG, "Invalid EAP Type for user credential: " + eapType); + return false; + } + + // Verify Non-EAP inner method for EAP-TTLS. + if (!SUPPORTED_AUTH.contains(nonEapInnerMethod)) { + Log.d(TAG, "Invalid non-EAP inner method for EAP-TTLS: " + nonEapInnerMethod); + return false; + } + return true; + } + public static final Creator<UserCredential> CREATOR = new Creator<UserCredential>() { @Override @@ -125,12 +197,22 @@ public final class Credential implements Parcelable { public UserCredential userCredential = null; /** - * Certificate based credential. + * Certificate based credential. This is used for EAP-TLS. * Contains fields under PerProviderSubscription/Credential/DigitalCertificate subtree. */ public static final class CertificateCredential implements Parcelable { /** - * Certificate type. Valid values are "802.1ar" and "x509v3". + * Supported certificate types. + */ + private static final String CERT_TYPE_X509V3 = "x509v3"; + + /** + * Certificate SHA-256 fingerprint length. + */ + private static final int CERT_SHA256_FINGER_PRINT_LENGTH = 32; + + /** + * Certificate type. */ public String certType = null; @@ -164,6 +246,24 @@ public final class Credential implements Parcelable { Arrays.equals(certSha256FingerPrint, that.certSha256FingerPrint); } + /** + * Validate the configuration data. + * + * @return true on success or false on failure + */ + public boolean validate() { + if (!TextUtils.equals(CERT_TYPE_X509V3, certType)) { + Log.d(TAG, "Unsupported certificate type: " + certType); + return false; + } + if (certSha256FingerPrint == null || + certSha256FingerPrint.length != CERT_SHA256_FINGER_PRINT_LENGTH) { + Log.d(TAG, "Invalid SHA-256 fingerprint"); + return false; + } + return true; + } + public static final Creator<CertificateCredential> CREATOR = new Creator<CertificateCredential>() { @Override @@ -188,7 +288,14 @@ public final class Credential implements Parcelable { */ public static final class SimCredential implements Parcelable { /** - * International Mobile device Subscriber Identity. + * Maximum string length for IMSI. + */ + public static final int MAX_IMSI_LENGTH = 15; + + /** + * International Mobile Subscriber Identity, is used to identify the user + * of a cellular network and is a unique identification associated with all + * cellular networks */ public String imsi = null; @@ -225,6 +332,26 @@ public final class Credential implements Parcelable { dest.writeInt(eapType); } + /** + * Validate the configuration data. + * + * @return true on success or false on failure + */ + public boolean validate() { + // Note: this only validate the format of IMSI string itself. Additional verification + // will be done by WifiService at the time of provisioning to verify against the IMSI + // of the SIM card installed in the device. + if (!verifyImsi()) { + return false; + } + if (eapType != EAPConstants.EAP_SIM && eapType != EAPConstants.EAP_AKA && + eapType != EAPConstants.EAP_AKA_PRIME) { + Log.d(TAG, "Invalid EAP Type for SIM credential: " + eapType); + return false; + } + return true; + } + public static final Creator<SimCredential> CREATOR = new Creator<SimCredential>() { @Override @@ -240,6 +367,43 @@ public final class Credential implements Parcelable { return new SimCredential[size]; } }; + + /** + * Verify the IMSI (International Mobile Subscriber Identity) string. The string + * should contain zero or more numeric digits, and might ends with a "*" for prefix + * matching. + * + * @return true if IMSI is valid, false otherwise. + */ + private boolean verifyImsi() { + if (TextUtils.isEmpty(imsi)) { + Log.d(TAG, "Missing IMSI"); + return false; + } + if (imsi.length() > MAX_IMSI_LENGTH) { + Log.d(TAG, "IMSI exceeding maximum length: " + imsi.length()); + return false; + } + + // Locate the first non-digit character. + int nonDigit; + char stopChar = '\0'; + for (nonDigit = 0; nonDigit < imsi.length(); nonDigit++) { + stopChar = imsi.charAt(nonDigit); + if (stopChar < '0' || stopChar > '9') { + break; + } + } + + if (nonDigit == imsi.length()) { + return true; + } + else if (nonDigit == imsi.length()-1 && stopChar == '*') { + // Prefix matching. + return true; + } + return false; + } } public SimCredential simCredential = null; @@ -296,6 +460,42 @@ public final class Credential implements Parcelable { isPrivateKeyEquals(clientPrivateKey, that.clientPrivateKey); } + /** + * Validate the configuration data. + * + * @return true on success or false on failure + */ + public boolean validate() { + if (TextUtils.isEmpty(realm)) { + Log.d(TAG, "Missing realm"); + return false; + } + if (realm.length() > MAX_REALM_LENGTH) { + Log.d(TAG, "realm exceeding maximum length: " + realm.length()); + return false; + } + + // Verify the credential. + if (userCredential != null) { + if (!verifyUserCredential()) { + return false; + } + } else if (certCredential != null) { + if (!verifyCertCredential()) { + return false; + } + } else if (simCredential != null) { + if (!verifySimCredential()) { + return false; + } + } else { + Log.d(TAG, "Missing required credential"); + return false; + } + + return true; + } + public static final Creator<Credential> CREATOR = new Creator<Credential>() { @Override @@ -317,6 +517,91 @@ public final class Credential implements Parcelable { } }; + /** + * Verify user credential. + * + * @return true if user credential is valid, false otherwise. + */ + private boolean verifyUserCredential() { + if (userCredential == null) { + Log.d(TAG, "Missing user credential"); + return false; + } + if (certCredential != null || simCredential != null) { + Log.d(TAG, "Contained more than one type of credential"); + return false; + } + if (!userCredential.validate()) { + return false; + } + if (caCertificate == null) { + Log.d(TAG, "Missing CA Certificate for user credential"); + return false; + } + return true; + } + + /** + * Verify certificate credential, which is used for EAP-TLS. This will verify + * that the necessary client key and certificates are provided. + * + * @return true if certificate credential is valid, false otherwise. + */ + private boolean verifyCertCredential() { + if (certCredential == null) { + Log.d(TAG, "Missing certificate credential"); + return false; + } + if (userCredential != null || simCredential != null) { + Log.d(TAG, "Contained more than one type of credential"); + return false; + } + + if (!certCredential.validate()) { + return false; + } + + // Verify required key and certificates for certificate credential. + if (caCertificate == null) { + Log.d(TAG, "Missing CA Certificate for certificate credential"); + return false; + } + if (clientPrivateKey == null) { + Log.d(TAG, "Missing client private key for certificate credential"); + return false; + } + try { + // Verify SHA-256 fingerprint for client certificate. + if (!verifySha256Fingerprint(clientCertificateChain, + certCredential.certSha256FingerPrint)) { + Log.d(TAG, "SHA-256 fingerprint mismatch"); + return false; + } + } catch (NoSuchAlgorithmException | CertificateEncodingException e) { + Log.d(TAG, "Failed to verify SHA-256 fingerprint: " + e.getMessage()); + return false; + } + + return true; + } + + /** + * Verify SIM credential. + * + * @return true if SIM credential is valid, false otherwise. + */ + private boolean verifySimCredential() { + if (simCredential == null) { + Log.d(TAG, "Missing SIM credential"); + return false; + } + if (userCredential != null || certCredential != null) { + Log.d(TAG, "Contained more than one type of credential"); + return false; + } + return simCredential.validate(); + } + private static boolean isPrivateKeyEquals(PrivateKey key1, PrivateKey key2) { if (key1 == null && key2 == null) { return true; @@ -373,4 +658,31 @@ public final class Credential implements Parcelable { return true; } + + /** + * Verify that the digest for a certificate in the certificate chain matches expected + * fingerprint. The certificate that matches the fingerprint is the client certificate. + * + * @param certChain Chain of certificates + * @param expectedFingerprint The expected SHA-256 digest of the client certificate + * @return true if the certificate chain contains a matching certificate, false otherwise + * @throws NoSuchAlgorithmException + * @throws CertificateEncodingException + */ + private static boolean verifySha256Fingerprint(X509Certificate[] certChain, + byte[] expectedFingerprint) + throws NoSuchAlgorithmException, CertificateEncodingException { + if (certChain == null) { + return false; + } + MessageDigest digester = MessageDigest.getInstance("SHA-256"); + for (X509Certificate certificate : certChain) { + digester.reset(); + byte[] fingerprint = digester.digest(certificate.getEncoded()); + if (Arrays.equals(expectedFingerprint, fingerprint)) { + return true; + } + } + return false; + } } diff --git a/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.java b/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.java index 2acc8bec8007..5837c06499e3 100644 --- a/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.java +++ b/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.java @@ -19,6 +19,7 @@ package android.net.wifi.hotspot2.pps; import android.os.Parcelable; import android.os.Parcel; import android.text.TextUtils; +import android.util.Log; import java.util.Arrays; @@ -34,6 +35,8 @@ import java.util.Arrays; * @hide */ public final class HomeSP implements Parcelable { + private static final String TAG = "HomeSP"; + /** * FQDN (Fully Qualified Domain Name) of this home service provider. */ @@ -77,6 +80,23 @@ public final class HomeSP implements Parcelable { Arrays.equals(roamingConsortiumOIs, that.roamingConsortiumOIs); } + /** + * Validate HomeSP data. + * + * @return true on success or false on failure + */ + public boolean validate() { + if (TextUtils.isEmpty(fqdn)) { + Log.d(TAG, "Missing FQDN"); + return false; + } + if (TextUtils.isEmpty(friendlyName)) { + Log.d(TAG, "Missing friendly name"); + return false; + } + return true; + } + public static final Creator<HomeSP> CREATOR = new Creator<HomeSP>() { @Override diff --git a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java index be11f0ee64c0..b4a3acf08975 100644 --- a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java +++ b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java @@ -16,8 +16,10 @@ package android.net.wifi.hotspot2; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import android.net.wifi.EAPConstants; import android.net.wifi.hotspot2.pps.Credential; import android.net.wifi.hotspot2.pps.HomeSP; import android.os.Parcel; @@ -44,7 +46,9 @@ public class PasspointConfigurationTest { cred.realm = "realm"; cred.userCredential = null; cred.certCredential = null; - cred.simCredential = null; + cred.simCredential = new Credential.SimCredential(); + cred.simCredential.imsi = "1234*"; + cred.simCredential.eapType = EAPConstants.EAP_SIM; cred.caCertificate = null; cred.clientCertificateChain = null; cred.clientPrivateKey = null; @@ -61,11 +65,20 @@ public class PasspointConfigurationTest { assertTrue(readConfig.equals(writeConfig)); } + /** + * Verify parcel read/write for a default configuration. + * + * @throws Exception + */ @Test public void verifyParcelWithDefault() throws Exception { verifyParcel(new PasspointConfiguration()); } + /** + * Verify parcel read/write for a configuration that contained both HomeSP and Credential. + * @throws Exception + */ @Test public void verifyParcelWithHomeSPAndCredential() throws Exception { PasspointConfiguration config = new PasspointConfiguration(); @@ -74,6 +87,11 @@ public class PasspointConfigurationTest { verifyParcel(config); } + /** + * Verify parcel read/write for a configuration that contained only HomeSP. + * + * @throws Exception + */ @Test public void verifyParcelWithHomeSPOnly() throws Exception { PasspointConfiguration config = new PasspointConfiguration(); @@ -81,10 +99,63 @@ public class PasspointConfigurationTest { verifyParcel(config); } + /** + * Verify parcel read/write for a configuration that contained only Credential. + * + * @throws Exception + */ @Test public void verifyParcelWithCredentialOnly() throws Exception { PasspointConfiguration config = new PasspointConfiguration(); config.credential = createCredential(); verifyParcel(config); } + + /** + * Verify that a default/empty configuration is invalid. + * + * @throws Exception + */ + @Test + public void validateDefaultConfig() throws Exception { + PasspointConfiguration config = new PasspointConfiguration(); + assertFalse(config.validate()); + } + + /** + * Verify that a configuration without Credential is invalid. + * + * @throws Exception + */ + @Test + public void validateConfigWithoutCredential() throws Exception { + PasspointConfiguration config = new PasspointConfiguration(); + config.homeSp = createHomeSp(); + assertFalse(config.validate()); + } + + /** + * Verify that a a configuration without HomeSP is invalid. + * + * @throws Exception + */ + @Test + public void validateConfigWithoutHomeSp() throws Exception { + PasspointConfiguration config = new PasspointConfiguration(); + config.credential = createCredential(); + assertFalse(config.validate()); + } + + /** + * Verify a valid configuration. + * + * @throws Exception + */ + @Test + public void validateValidConfig() throws Exception { + PasspointConfiguration config = new PasspointConfiguration(); + config.homeSp = createHomeSp(); + config.credential = createCredential(); + assertTrue(config.validate()); + } }
\ No newline at end of file diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java index 68ac4efa214f..223aa5231b36 100644 --- a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java +++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java @@ -16,14 +16,19 @@ package android.net.wifi.hotspot2.pps; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import android.net.wifi.EAPConstants; import android.net.wifi.FakeKeys; import android.os.Parcel; import android.test.suitebuilder.annotation.SmallTest; +import android.util.Log; +import java.security.MessageDigest; import java.security.PrivateKey; import java.security.cert.X509Certificate; +import java.util.Arrays; import org.junit.Test; @@ -52,15 +57,15 @@ public class CredentialTest { private static Credential createCredentialWithCertificateCredential() { Credential.CertificateCredential certCred = new Credential.CertificateCredential(); certCred.certType = "x509v3"; - certCred.certSha256FingerPrint = new byte[256]; + certCred.certSha256FingerPrint = new byte[32]; return createCredential(null, certCred, null, FakeKeys.CA_CERT0, new X509Certificate[] {FakeKeys.CLIENT_CERT}, FakeKeys.RSA_KEY1); } private static Credential createCredentialWithSimCredential() { Credential.SimCredential simCred = new Credential.SimCredential(); - simCred.imsi = "imsi"; - simCred.eapType = 1; + simCred.imsi = "1234*"; + simCred.eapType = EAPConstants.EAP_SIM; return createCredential(null, null, simCred, null, null, null); } @@ -68,7 +73,7 @@ public class CredentialTest { Credential.UserCredential userCred = new Credential.UserCredential(); userCred.username = "username"; userCred.password = "password"; - userCred.eapType = 1; + userCred.eapType = EAPConstants.EAP_TTLS; userCred.nonEapInnerMethod = "MS-CHAP"; return createCredential(userCred, null, null, FakeKeys.CA_CERT0, new X509Certificate[] {FakeKeys.CLIENT_CERT}, FakeKeys.RSA_KEY1); @@ -83,23 +88,386 @@ public class CredentialTest { assertTrue(readCred.equals(writeCred)); } + /** + * Verify parcel read/write for a default/empty credential. + * + * @throws Exception + */ @Test public void verifyParcelWithDefault() throws Exception { verifyParcel(new Credential()); } + /** + * Verify parcel read/write for a certificate credential. + * + * @throws Exception + */ @Test public void verifyParcelWithCertificateCredential() throws Exception { verifyParcel(createCredentialWithCertificateCredential()); } + /** + * Verify parcel read/write for a SIM credential. + * + * @throws Exception + */ @Test public void verifyParcelWithSimCredential() throws Exception { verifyParcel(createCredentialWithSimCredential()); } + /** + * Verify parcel read/write for an user credential. + * + * @throws Exception + */ @Test public void verifyParcelWithUserCredential() throws Exception { verifyParcel(createCredentialWithUserCredential()); } -} + + /** + * Verify a valid user credential. + * @throws Exception + */ + @Test + public void validateUserCredential() throws Exception { + Credential cred = new Credential(); + cred.realm = "realm"; + cred.userCredential = new Credential.UserCredential(); + cred.userCredential.username = "username"; + cred.userCredential.password = "password"; + cred.userCredential.eapType = EAPConstants.EAP_TTLS; + cred.userCredential.nonEapInnerMethod = "MS-CHAP"; + cred.caCertificate = FakeKeys.CA_CERT0; + assertTrue(cred.validate()); + } + + /** + * Verify that an user credential without CA Certificate is invalid. + * + * @throws Exception + */ + @Test + public void validateUserCredentialWithoutCaCert() throws Exception { + Credential cred = new Credential(); + cred.realm = "realm"; + cred.userCredential = new Credential.UserCredential(); + cred.userCredential.username = "username"; + cred.userCredential.password = "password"; + cred.userCredential.eapType = EAPConstants.EAP_TTLS; + cred.userCredential.nonEapInnerMethod = "MS-CHAP"; + assertFalse(cred.validate()); + } + + /** + * Verify that an user credential with EAP type other than EAP-TTLS is invalid. + * + * @throws Exception + */ + @Test + public void validateUserCredentialWithEapTls() throws Exception { + Credential cred = new Credential(); + cred.realm = "realm"; + cred.userCredential = new Credential.UserCredential(); + cred.userCredential.username = "username"; + cred.userCredential.password = "password"; + cred.userCredential.eapType = EAPConstants.EAP_TLS; + cred.userCredential.nonEapInnerMethod = "MS-CHAP"; + cred.caCertificate = FakeKeys.CA_CERT0; + assertFalse(cred.validate()); + } + + + /** + * Verify that an user credential without realm is invalid. + * + * @throws Exception + */ + @Test + public void validateUserCredentialWithoutRealm() throws Exception { + Credential cred = new Credential(); + cred.userCredential = new Credential.UserCredential(); + cred.userCredential.username = "username"; + cred.userCredential.password = "password"; + cred.userCredential.eapType = EAPConstants.EAP_TTLS; + cred.userCredential.nonEapInnerMethod = "MS-CHAP"; + cred.caCertificate = FakeKeys.CA_CERT0; + assertFalse(cred.validate()); + } + + /** + * Verify that an user credential without username is invalid. + * + * @throws Exception + */ + @Test + public void validateUserCredentialWithoutUsername() throws Exception { + Credential cred = new Credential(); + cred.realm = "realm"; + cred.userCredential = new Credential.UserCredential(); + cred.userCredential.password = "password"; + cred.userCredential.eapType = EAPConstants.EAP_TTLS; + cred.userCredential.nonEapInnerMethod = "MS-CHAP"; + cred.caCertificate = FakeKeys.CA_CERT0; + assertFalse(cred.validate()); + } + + /** + * Verify that an user credential without password is invalid. + * + * @throws Exception + */ + @Test + public void validateUserCredentialWithoutPassword() throws Exception { + Credential cred = new Credential(); + cred.realm = "realm"; + cred.userCredential = new Credential.UserCredential(); + cred.userCredential.username = "username"; + cred.userCredential.eapType = EAPConstants.EAP_TTLS; + cred.userCredential.nonEapInnerMethod = "MS-CHAP"; + cred.caCertificate = FakeKeys.CA_CERT0; + assertFalse(cred.validate()); + } + + /** + * Verify that an user credential without auth methoh (non-EAP inner method) is invalid. + * + * @throws Exception + */ + @Test + public void validateUserCredentialWithoutAuthMethod() throws Exception { + Credential cred = new Credential(); + cred.realm = "realm"; + cred.userCredential = new Credential.UserCredential(); + cred.userCredential.username = "username"; + cred.userCredential.password = "password"; + cred.userCredential.eapType = EAPConstants.EAP_TTLS; + cred.caCertificate = FakeKeys.CA_CERT0; + assertFalse(cred.validate()); + } + + /** + * Verify a certificate credential. CA Certificate, client certificate chain, + * and client private key are all required. Also the digest for client + * certificate must match the fingerprint specified in the certificate credential. + * + * @throws Exception + */ + @Test + public void validateCertCredential() throws Exception { + Credential cred = new Credential(); + cred.realm = "realm"; + // Setup certificate credential. + cred.certCredential = new Credential.CertificateCredential(); + cred.certCredential.certType = "x509v3"; + cred.certCredential.certSha256FingerPrint = + MessageDigest.getInstance("SHA-256").digest(FakeKeys.CLIENT_CERT.getEncoded()); + // Setup certificates and private key. + cred.caCertificate = FakeKeys.CA_CERT0; + cred.clientCertificateChain = new X509Certificate[] {FakeKeys.CLIENT_CERT}; + cred.clientPrivateKey = FakeKeys.RSA_KEY1; + assertTrue(cred.validate()); + } + + /** + * Verify that an certificate credential without CA Certificate is invalid. + * + * @throws Exception + */ + public void validateCertCredentialWithoutCaCert() throws Exception { + Credential cred = new Credential(); + cred.realm = "realm"; + // Setup certificate credential. + cred.certCredential = new Credential.CertificateCredential(); + cred.certCredential.certType = "x509v3"; + cred.certCredential.certSha256FingerPrint = + MessageDigest.getInstance("SHA-256").digest(FakeKeys.CLIENT_CERT.getEncoded()); + // Setup certificates and private key. + cred.clientCertificateChain = new X509Certificate[] {FakeKeys.CLIENT_CERT}; + cred.clientPrivateKey = FakeKeys.RSA_KEY1; + assertFalse(cred.validate()); + } + + /** + * Verify that a certificate credential without client certificate chain is invalid. + * + * @throws Exception + */ + @Test + public void validateCertCredentialWithoutClientCertChain() throws Exception { + Credential cred = new Credential(); + cred.realm = "realm"; + // Setup certificate credential. + cred.certCredential = new Credential.CertificateCredential(); + cred.certCredential.certType = "x509v3"; + cred.certCredential.certSha256FingerPrint = + MessageDigest.getInstance("SHA-256").digest(FakeKeys.CLIENT_CERT.getEncoded()); + // Setup certificates and private key. + cred.caCertificate = FakeKeys.CA_CERT0; + cred.clientPrivateKey = FakeKeys.RSA_KEY1; + assertFalse(cred.validate()); + } + + /** + * Verify that a certificate credential without client private key is invalid. + * + * @throws Exception + */ + @Test + public void validateCertCredentialWithoutClientPrivateKey() throws Exception { + Credential cred = new Credential(); + cred.realm = "realm"; + // Setup certificate credential. + cred.certCredential = new Credential.CertificateCredential(); + cred.certCredential.certType = "x509v3"; + cred.certCredential.certSha256FingerPrint = + MessageDigest.getInstance("SHA-256").digest(FakeKeys.CLIENT_CERT.getEncoded()); + // Setup certificates and private key. + cred.caCertificate = FakeKeys.CA_CERT0; + cred.clientCertificateChain = new X509Certificate[] {FakeKeys.CLIENT_CERT}; + assertFalse(cred.validate()); + } + + /** + * Verify that a certificate credential with mismatch client certificate fingerprint + * is invalid. + * + * @throws Exception + */ + @Test + public void validateCertCredentialWithMismatchFingerprint() throws Exception { + Credential cred = new Credential(); + cred.realm = "realm"; + // Setup certificate credential. + cred.certCredential = new Credential.CertificateCredential(); + cred.certCredential.certType = "x509v3"; + cred.certCredential.certSha256FingerPrint = new byte[32]; + Arrays.fill(cred.certCredential.certSha256FingerPrint, (byte)0); + // Setup certificates and private key. + cred.caCertificate = FakeKeys.CA_CERT0; + cred.clientCertificateChain = new X509Certificate[] {FakeKeys.CLIENT_CERT}; + cred.clientPrivateKey = FakeKeys.RSA_KEY1; + assertFalse(cred.validate()); + } + + /** + * Verify a SIM credential using EAP-SIM. + * + * @throws Exception + */ + @Test + public void validateSimCredentialWithEapSim() throws Exception { + Credential cred = new Credential(); + cred.realm = "realm"; + // Setup SIM credential. + cred.simCredential = new Credential.SimCredential(); + cred.simCredential.imsi = "1234*"; + cred.simCredential.eapType = EAPConstants.EAP_SIM; + assertTrue(cred.validate()); + } + + /** + * Verify a SIM credential using EAP-AKA. + * + * @throws Exception + */ + @Test + public void validateSimCredentialWithEapAka() throws Exception { + Credential cred = new Credential(); + cred.realm = "realm"; + // Setup SIM credential. + cred.simCredential = new Credential.SimCredential(); + cred.simCredential.imsi = "1234*"; + cred.simCredential.eapType = EAPConstants.EAP_AKA; + assertTrue(cred.validate()); + } + + /** + * Verify a SIM credential using EAP-AKA-PRIME. + * + * @throws Exception + */ + @Test + public void validateSimCredentialWithEapAkaPrime() throws Exception { + Credential cred = new Credential(); + cred.realm = "realm"; + // Setup SIM credential. + cred.simCredential = new Credential.SimCredential(); + cred.simCredential.imsi = "1234*"; + cred.simCredential.eapType = EAPConstants.EAP_AKA_PRIME; + assertTrue(cred.validate()); + } + + /** + * Verify that a SIM credential without IMSI is invalid. + * + * @throws Exception + */ + @Test + public void validateSimCredentialWithoutIMSI() throws Exception { + Credential cred = new Credential(); + cred.realm = "realm"; + // Setup SIM credential. + cred.simCredential = new Credential.SimCredential(); + cred.simCredential.eapType = EAPConstants.EAP_SIM; + assertFalse(cred.validate()); + } + + /** + * Verify that a SIM credential with an invalid IMSI is invalid. + * + * @throws Exception + */ + @Test + public void validateSimCredentialWithInvalidIMSI() throws Exception { + Credential cred = new Credential(); + cred.realm = "realm"; + // Setup SIM credential. + cred.simCredential = new Credential.SimCredential(); + cred.simCredential.imsi = "dummy"; + cred.simCredential.eapType = EAPConstants.EAP_SIM; + assertFalse(cred.validate()); + } + + /** + * Verify that a SIM credential with invalid EAP type is invalid. + * + * @throws Exception + */ + @Test + public void validateSimCredentialWithEapTls() throws Exception { + Credential cred = new Credential(); + cred.realm = "realm"; + // Setup SIM credential. + cred.simCredential = new Credential.SimCredential(); + cred.simCredential.imsi = "1234*"; + cred.simCredential.eapType = EAPConstants.EAP_TLS; + assertFalse(cred.validate()); + } + + /** + * Verify that a credential contained both an user and a SIM credential is invalid. + * + * @throws Exception + */ + @Test + public void validateCredentialWithUserAndSimCredential() throws Exception { + Credential cred = new Credential(); + cred.realm = "realm"; + // Setup user credential with EAP-TTLS. + cred.userCredential = new Credential.UserCredential(); + cred.userCredential.username = "username"; + cred.userCredential.password = "password"; + cred.userCredential.eapType = EAPConstants.EAP_TTLS; + cred.userCredential.nonEapInnerMethod = "MS-CHAP"; + cred.caCertificate = FakeKeys.CA_CERT0; + // Setup SIM credential. + cred.simCredential = new Credential.SimCredential(); + cred.simCredential.imsi = "1234*"; + cred.simCredential.eapType = EAPConstants.EAP_SIM; + assertFalse(cred.validate()); + } +}
\ No newline at end of file diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSPTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSPTest.java index 0d2da6404e7f..fff1477e833b 100644 --- a/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSPTest.java +++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSPTest.java @@ -16,13 +16,12 @@ package android.net.wifi.hotspot2.pps; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import android.os.Parcel; import android.test.suitebuilder.annotation.SmallTest; -import java.util.HashMap; - import org.junit.Test; /** @@ -47,13 +46,76 @@ public class HomeSPTest { assertTrue(readHomeSp.equals(writeHomeSp)); } + /** + * Verify parcel read/write for an empty HomeSP. + * + * @throws Exception + */ @Test public void verifyParcelWithEmptyHomeSP() throws Exception { verifyParcel(new HomeSP()); } + /** + * Verify parcel read/write for a valid HomeSP. + * + * @throws Exception + */ @Test public void verifyParcelWithValidHomeSP() throws Exception { verifyParcel(createHomeSp()); } + + /** + * Verify that a HomeSP is valid when both FQDN and Friendly Name + * are provided. + * + * @throws Exception + */ + @Test + public void validateValidHomeSP() throws Exception { + HomeSP homeSp = new HomeSP(); + homeSp.fqdn = "fqdn"; + homeSp.friendlyName = "friendly name"; + assertTrue(homeSp.validate()); + } + + /** + * Verify that a HomeSP is not valid when FQDN is not provided + * + * @throws Exception + */ + @Test + public void validateHomeSpWithoutFqdn() throws Exception { + HomeSP homeSp = new HomeSP(); + homeSp.friendlyName = "friendly name"; + assertFalse(homeSp.validate()); + } + + /** + * Verify that a HomeSP is not valid when Friendly Name is not provided + * + * @throws Exception + */ + @Test + public void validateHomeSpWithoutFriendlyName() throws Exception { + HomeSP homeSp = new HomeSP(); + homeSp.fqdn = "fqdn"; + assertFalse(homeSp.validate()); + } + + /** + * Verify that a HomeSP is valid when the optional Roaming Consortium OIs are + * provided. + * + * @throws Exception + */ + @Test + public void validateHomeSpWithRoamingConsoritums() throws Exception { + HomeSP homeSp = new HomeSP(); + homeSp.fqdn = "fqdn"; + homeSp.friendlyName = "friendly name"; + homeSp.roamingConsortiumOIs = new long[] {0x55, 0x66}; + assertTrue(homeSp.validate()); + } } |