diff --git a/Patches/LineageOS-20.0/android_frameworks_av/ASB-2023-06/0001-Fix-NuMediaExtractor-readSampleData-buffer-Handling.patch b/Patches/LineageOS-20.0/android_frameworks_av/ASB-2023-06/0001-Fix-NuMediaExtractor-readSampleData-buffer-Handling.patch deleted file mode 100644 index cf5b7f3e..00000000 --- a/Patches/LineageOS-20.0/android_frameworks_av/ASB-2023-06/0001-Fix-NuMediaExtractor-readSampleData-buffer-Handling.patch +++ /dev/null @@ -1,75 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Ray Essick -Date: Mon, 27 Mar 2023 18:16:46 -0500 -Subject: [PATCH] Fix NuMediaExtractor::readSampleData buffer Handling - -readSampleData() did not initialize buffer before filling it, -leading to OOB memory references. Correct and clarify the book -keeping around output buffer management. - -Bug: 275418191 -Test: CtsMediaExtractorTestCases w/debug messages -(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:943fc12219b21d2a98f0ddc070b9b316a6f5d412) -(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:84c69bca81175feb2fd97ebb22e432ee41572786) -Merged-In: Ie744f118526f100d82a312c64f7c6fcf20773b6d -Change-Id: Ie744f118526f100d82a312c64f7c6fcf20773b6d ---- - media/libstagefright/NuMediaExtractor.cpp | 14 +++++++++----- - 1 file changed, 9 insertions(+), 5 deletions(-) - -diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp -index 2b45f2d16d..5b39618ad7 100644 ---- a/media/libstagefright/NuMediaExtractor.cpp -+++ b/media/libstagefright/NuMediaExtractor.cpp -@@ -639,9 +639,11 @@ status_t NuMediaExtractor::appendVorbisNumPageSamples( - numPageSamples = -1; - } - -+ // insert, including accounting for the space used. - memcpy((uint8_t *)buffer->data() + mbuf->range_length(), - &numPageSamples, - sizeof(numPageSamples)); -+ buffer->setRange(buffer->offset(), buffer->size() + sizeof(numPageSamples)); - - uint32_t type; - const void *data; -@@ -690,6 +692,8 @@ status_t NuMediaExtractor::readSampleData(const sp &buffer) { - - ssize_t minIndex = fetchAllTrackSamples(); - -+ buffer->setRange(0, 0); // start with an empty buffer -+ - if (minIndex < 0) { - return ERROR_END_OF_STREAM; - } -@@ -705,25 +709,25 @@ status_t NuMediaExtractor::readSampleData(const sp &buffer) { - sampleSize += sizeof(int32_t); - } - -+ // capacity() is ok since we cleared out the buffer - if (buffer->capacity() < sampleSize) { - return -ENOMEM; - } - -+ const size_t srclen = it->mBuffer->range_length(); - const uint8_t *src = - (const uint8_t *)it->mBuffer->data() - + it->mBuffer->range_offset(); - -- memcpy((uint8_t *)buffer->data(), src, it->mBuffer->range_length()); -+ memcpy((uint8_t *)buffer->data(), src, srclen); -+ buffer->setRange(0, srclen); - - status_t err = OK; - if (info->mTrackFlags & kIsVorbis) { -+ // adjusts range when it inserts the extra bits - err = appendVorbisNumPageSamples(it->mBuffer, buffer); - } - -- if (err == OK) { -- buffer->setRange(0, sampleSize); -- } -- - return err; - } - diff --git a/Patches/LineageOS-20.0/android_frameworks_base/ASB-2023-06/0001-Prevent-sharesheet-from-previewing-unowned-URIs.patch b/Patches/LineageOS-20.0/android_frameworks_base/ASB-2023-06/0001-Prevent-sharesheet-from-previewing-unowned-URIs.patch deleted file mode 100644 index dae662a8..00000000 --- a/Patches/LineageOS-20.0/android_frameworks_base/ASB-2023-06/0001-Prevent-sharesheet-from-previewing-unowned-URIs.patch +++ /dev/null @@ -1,105 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Mark Renouf -Date: Wed, 22 Feb 2023 14:48:51 +0000 -Subject: [PATCH 01/10] Prevent sharesheet from previewing unowned URIs - -Bug: 261036568 -Test: manually via supplied tool (see bug) -(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:fa83e125d14e458545086d16f2e7d1051812dabc) -Merged-In: Ib3f5839d00c7cf09bca3b01fc0a8a6f0f4960993 -Change-Id: Ib3f5839d00c7cf09bca3b01fc0a8a6f0f4960993 ---- - .../android/internal/app/ChooserActivity.java | 35 +++++++++++++++++-- - 1 file changed, 33 insertions(+), 2 deletions(-) - -diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java -index bfff93b5f7a4..b68e4f4956d0 100644 ---- a/core/java/com/android/internal/app/ChooserActivity.java -+++ b/core/java/com/android/internal/app/ChooserActivity.java -@@ -21,6 +21,7 @@ import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT - import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_SHARE_WITH_PERSONAL; - import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_SHARE_WITH_WORK; - import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CROSS_PROFILE_BLOCKED_TITLE; -+import static android.content.ContentProvider.getUserIdFromUri; - import static android.stats.devicepolicy.DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL; - import static android.stats.devicepolicy.DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK; - -@@ -161,6 +162,7 @@ import java.util.List; - import java.util.Map; - import java.util.Objects; - import java.util.function.Supplier; -+import java.util.stream.Collectors; - - /** - * The Chooser Activity handles intent resolution specifically for sharing intents - -@@ -1395,7 +1397,7 @@ public class ChooserActivity extends ResolverActivity implements - - ImageView previewThumbnailView = contentPreviewLayout.findViewById( - R.id.content_preview_thumbnail); -- if (previewThumbnail == null) { -+ if (!validForContentPreview(previewThumbnail)) { - previewThumbnailView.setVisibility(View.GONE); - } else { - mPreviewCoord = new ContentPreviewCoordinator(contentPreviewLayout, false); -@@ -1428,6 +1430,10 @@ public class ChooserActivity extends ResolverActivity implements - - if (Intent.ACTION_SEND.equals(action)) { - Uri uri = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM); -+ if (!validForContentPreview(uri)) { -+ contentPreviewLayout.setVisibility(View.GONE); -+ return contentPreviewLayout; -+ } - imagePreview.findViewById(R.id.content_preview_image_1_large) - .setTransitionName(ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME); - mPreviewCoord.loadUriIntoView(R.id.content_preview_image_1_large, uri, 0); -@@ -1437,7 +1443,7 @@ public class ChooserActivity extends ResolverActivity implements - List uris = targetIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM); - List imageUris = new ArrayList<>(); - for (Uri uri : uris) { -- if (isImageType(resolver.getType(uri))) { -+ if (validForContentPreview(uri) && isImageType(resolver.getType(uri))) { - imageUris.add(uri); - } - } -@@ -1547,9 +1553,16 @@ public class ChooserActivity extends ResolverActivity implements - String action = targetIntent.getAction(); - if (Intent.ACTION_SEND.equals(action)) { - Uri uri = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM); -+ if (!validForContentPreview(uri)) { -+ contentPreviewLayout.setVisibility(View.GONE); -+ return contentPreviewLayout; -+ } - loadFileUriIntoView(uri, contentPreviewLayout); - } else { - List uris = targetIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM); -+ uris = uris.stream() -+ .filter(ChooserActivity::validForContentPreview) -+ .collect(Collectors.toList()); - int uriCount = uris.size(); - - if (uriCount == 0) { -@@ -1608,6 +1621,24 @@ public class ChooserActivity extends ResolverActivity implements - } - } - -+ /** -+ * Indicate if the incoming content URI should be allowed. -+ * -+ * @param uri the uri to test -+ * @return true if the URI is allowed for content preview -+ */ -+ private static boolean validForContentPreview(Uri uri) throws SecurityException { -+ if (uri == null) { -+ return false; -+ } -+ int userId = getUserIdFromUri(uri, UserHandle.USER_CURRENT); -+ if (userId != UserHandle.USER_CURRENT && userId != UserHandle.myUserId()) { -+ Log.e(TAG, "dropped invalid content URI belonging to user " + userId); -+ return false; -+ } -+ return true; -+ } -+ - @VisibleForTesting - protected boolean isImageType(String mimeType) { - return mimeType != null && mimeType.startsWith("image/"); diff --git a/Patches/LineageOS-20.0/android_frameworks_base/ASB-2023-06/0002-Remove-Activity-if-it-enters-PiP-without-window.patch b/Patches/LineageOS-20.0/android_frameworks_base/ASB-2023-06/0002-Remove-Activity-if-it-enters-PiP-without-window.patch deleted file mode 100644 index 5f08903e..00000000 --- a/Patches/LineageOS-20.0/android_frameworks_base/ASB-2023-06/0002-Remove-Activity-if-it-enters-PiP-without-window.patch +++ /dev/null @@ -1,35 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Hongwei Wang -Date: Thu, 23 Feb 2023 13:23:37 -0800 -Subject: [PATCH 02/10] Remove Activity if it enters PiP without window - -This is to prevent malicious app entering PiP without being visible -first, like blocking onResume from completion. Which in turn -leaves the PiP window in limbo and non-interactable. - -Bug: 265293293 -Test: atest PinnedStackTests -(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:4fad1456409b79d6e649a29d5116a4fe3160bd21) -Merged-In: I458a9508662e72a1adb9d9818105f2e9d7096d44 -Change-Id: I458a9508662e72a1adb9d9818105f2e9d7096d44 ---- - .../core/java/com/android/server/wm/ActivityRecord.java | 6 ++++++ - 1 file changed, 6 insertions(+) - -diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java -index c1cbdef6b5f5..69e1511da7be 100644 ---- a/services/core/java/com/android/server/wm/ActivityRecord.java -+++ b/services/core/java/com/android/server/wm/ActivityRecord.java -@@ -1477,6 +1477,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A - mLastReportedMultiWindowMode = inPictureInPictureMode; - ensureActivityConfiguration(0 /* globalChanges */, PRESERVE_WINDOWS, - true /* ignoreVisibility */); -+ if (inPictureInPictureMode && findMainWindow() == null) { -+ // Prevent malicious app entering PiP without valid WindowState, which can in turn -+ // result a non-touchable PiP window since the InputConsumer for PiP requires it. -+ EventLog.writeEvent(0x534e4554, "265293293", -1, ""); -+ removeImmediately(); -+ } - } - } - diff --git a/Patches/LineageOS-20.0/android_frameworks_base/ASB-2023-06/0003-Wait-for-preloading-images-to-complete-before-inflat.patch b/Patches/LineageOS-20.0/android_frameworks_base/ASB-2023-06/0003-Wait-for-preloading-images-to-complete-before-inflat.patch deleted file mode 100644 index 5815fdc7..00000000 --- a/Patches/LineageOS-20.0/android_frameworks_base/ASB-2023-06/0003-Wait-for-preloading-images-to-complete-before-inflat.patch +++ /dev/null @@ -1,238 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Valentin Iftime -Date: Wed, 15 Feb 2023 20:39:44 +0100 -Subject: [PATCH 03/10] Wait for preloading images to complete before inflating - notifications - - NotificationContentInflater waits on SysUiBg thread for images to load, with a timeout - of 1000ms. - -Test: 1. Build a test app that posts MessagingStyle notifications with a huge image (8k+) set as data Uri. - 2. SystemUi should not ANR - 3. adb logcat | grep NotificationInlineImageCache - shows timeout/cancellation logs - -Bug: 252766417 -Bug: 223859644 -(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:2a2da8935ab70163044c34d7b0d9b9ed4cb91a76) -Merged-In: I341db60223214cf2282b5c0270e343e1ce95fa01 -Change-Id: I341db60223214cf2282b5c0270e343e1ce95fa01 ---- - .../row/NotificationContentInflater.java | 15 +++++- - .../row/NotificationInlineImageCache.java | 21 ++++++-- - .../row/NotificationInlineImageResolver.java | 49 +++++++++++++++++-- - 3 files changed, 76 insertions(+), 9 deletions(-) - -diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java -index c534860d12c6..d2fb0c142cc5 100644 ---- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java -+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java -@@ -439,6 +439,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder - CancellationSignal cancellationSignal = new CancellationSignal(); - cancellationSignal.setOnCancelListener( - () -> runningInflations.values().forEach(CancellationSignal::cancel)); -+ - return cancellationSignal; - } - -@@ -711,6 +712,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder - public static class AsyncInflationTask extends AsyncTask - implements InflationCallback, InflationTask { - -+ private static final long IMG_PRELOAD_TIMEOUT_MS = 1000L; - private final NotificationEntry mEntry; - private final Context mContext; - private final boolean mInflateSynchronously; -@@ -804,7 +806,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder - recoveredBuilder, mIsLowPriority, mUsesIncreasedHeight, - mUsesIncreasedHeadsUpHeight, packageContext); - InflatedSmartReplyState previousSmartReplyState = mRow.getExistingSmartReplyState(); -- return inflateSmartReplyViews( -+ InflationProgress result = inflateSmartReplyViews( - inflationProgress, - mReInflateFlags, - mEntry, -@@ -812,6 +814,11 @@ public class NotificationContentInflater implements NotificationRowContentBinder - packageContext, - previousSmartReplyState, - mSmartRepliesInflater); -+ -+ // wait for image resolver to finish preloading -+ mRow.getImageResolver().waitForPreloadedImages(IMG_PRELOAD_TIMEOUT_MS); -+ -+ return result; - } catch (Exception e) { - mError = e; - return null; -@@ -846,6 +853,9 @@ public class NotificationContentInflater implements NotificationRowContentBinder - mCallback.handleInflationException(mRow.getEntry(), - new InflationException("Couldn't inflate contentViews" + e)); - } -+ -+ // Cancel any image loading tasks, not useful any more -+ mRow.getImageResolver().cancelRunningTasks(); - } - - @Override -@@ -872,6 +882,9 @@ public class NotificationContentInflater implements NotificationRowContentBinder - // Notify the resolver that the inflation task has finished, - // try to purge unnecessary cached entries. - mRow.getImageResolver().purgeCache(); -+ -+ // Cancel any image loading tasks that have not completed at this point -+ mRow.getImageResolver().cancelRunningTasks(); - } - - private static class RtlEnabledContext extends ContextWrapper { -diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java -index 41eeada0fcda..fe0b3123eb25 100644 ---- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java -+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java -@@ -22,8 +22,11 @@ import android.os.AsyncTask; - import android.util.Log; - - import java.util.Set; -+import java.util.concurrent.CancellationException; - import java.util.concurrent.ConcurrentHashMap; - import java.util.concurrent.ExecutionException; -+import java.util.concurrent.TimeUnit; -+import java.util.concurrent.TimeoutException; - - /** - * A cache for inline images of image messages. -@@ -56,12 +59,13 @@ public class NotificationInlineImageCache implements NotificationInlineImageReso - } - - @Override -- public Drawable get(Uri uri) { -+ public Drawable get(Uri uri, long timeoutMs) { - Drawable result = null; - try { -- result = mCache.get(uri).get(); -- } catch (InterruptedException | ExecutionException ex) { -- Log.d(TAG, "get: Failed get image from " + uri); -+ result = mCache.get(uri).get(timeoutMs, TimeUnit.MILLISECONDS); -+ } catch (InterruptedException | ExecutionException -+ | TimeoutException | CancellationException ex) { -+ Log.d(TAG, "get: Failed get image from " + uri + " " + ex); - } - return result; - } -@@ -72,6 +76,15 @@ public class NotificationInlineImageCache implements NotificationInlineImageReso - mCache.entrySet().removeIf(entry -> !wantedSet.contains(entry.getKey())); - } - -+ @Override -+ public void cancelRunningTasks() { -+ mCache.forEach((key, value) -> { -+ if (value.getStatus() != AsyncTask.Status.FINISHED) { -+ value.cancel(true); -+ } -+ }); -+ } -+ - private static class PreloadImageTask extends AsyncTask { - private final NotificationInlineImageResolver mResolver; - -diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java -index b05e64ab1991..c620f448b3b7 100644 ---- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java -+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java -@@ -23,6 +23,7 @@ import android.graphics.drawable.Drawable; - import android.net.Uri; - import android.os.Bundle; - import android.os.Parcelable; -+import android.os.SystemClock; - import android.util.Log; - - import com.android.internal.R; -@@ -45,6 +46,9 @@ import java.util.Set; - public class NotificationInlineImageResolver implements ImageResolver { - private static final String TAG = NotificationInlineImageResolver.class.getSimpleName(); - -+ // Timeout for loading images from ImageCache when calling from UI thread -+ private static final long MAX_UI_THREAD_TIMEOUT_MS = 100L; -+ - private final Context mContext; - private final ImageCache mImageCache; - private Set mWantedUriSet; -@@ -123,17 +127,25 @@ public class NotificationInlineImageResolver implements ImageResolver { - return null; - } - -+ /** -+ * Loads an image from the Uri. -+ * This method is synchronous and is usually called from the Main thread. -+ * It will time-out after MAX_UI_THREAD_TIMEOUT_MS. -+ * -+ * @param uri Uri of the target image. -+ * @return drawable of the image, null if loading failed/timeout -+ */ - @Override - public Drawable loadImage(Uri uri) { -- return hasCache() ? loadImageFromCache(uri) : resolveImage(uri); -+ return hasCache() ? loadImageFromCache(uri, MAX_UI_THREAD_TIMEOUT_MS) : resolveImage(uri); - } - -- private Drawable loadImageFromCache(Uri uri) { -+ private Drawable loadImageFromCache(Uri uri, long timeoutMs) { - // if the uri isn't currently cached, try caching it first - if (!mImageCache.hasEntry(uri)) { - mImageCache.preload((uri)); - } -- return mImageCache.get(uri); -+ return mImageCache.get(uri, timeoutMs); - } - - /** -@@ -207,6 +219,30 @@ public class NotificationInlineImageResolver implements ImageResolver { - return mWantedUriSet; - } - -+ /** -+ * Wait for a maximum timeout for images to finish preloading -+ * @param timeoutMs total timeout time -+ */ -+ void waitForPreloadedImages(long timeoutMs) { -+ if (!hasCache()) { -+ return; -+ } -+ Set preloadedUris = getWantedUriSet(); -+ if (preloadedUris != null) { -+ // Decrement remaining timeout after each image check -+ long endTimeMs = SystemClock.elapsedRealtime() + timeoutMs; -+ preloadedUris.forEach( -+ uri -> loadImageFromCache(uri, endTimeMs - SystemClock.elapsedRealtime())); -+ } -+ } -+ -+ void cancelRunningTasks() { -+ if (!hasCache()) { -+ return; -+ } -+ mImageCache.cancelRunningTasks(); -+ } -+ - /** - * A interface for internal cache implementation of this resolver. - */ -@@ -216,7 +252,7 @@ public class NotificationInlineImageResolver implements ImageResolver { - * @param uri The uri of the image. - * @return Drawable of the image. - */ -- Drawable get(Uri uri); -+ Drawable get(Uri uri, long timeoutMs); - - /** - * Set the image resolver that actually resolves image from specified uri. -@@ -241,6 +277,11 @@ public class NotificationInlineImageResolver implements ImageResolver { - * Purge unnecessary entries in the cache. - */ - void purge(); -+ -+ /** -+ * Cancel all unfinished image loading tasks -+ */ -+ void cancelRunningTasks(); - } - - } diff --git a/Patches/LineageOS-20.0/android_frameworks_base/ASB-2023-06/0004-Prevent-RemoteViews-crashing-SystemUi.patch b/Patches/LineageOS-20.0/android_frameworks_base/ASB-2023-06/0004-Prevent-RemoteViews-crashing-SystemUi.patch deleted file mode 100644 index 06d86ec2..00000000 --- a/Patches/LineageOS-20.0/android_frameworks_base/ASB-2023-06/0004-Prevent-RemoteViews-crashing-SystemUi.patch +++ /dev/null @@ -1,290 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Valentin Iftime -Date: Wed, 22 Feb 2023 09:38:55 +0100 -Subject: [PATCH 04/10] Prevent RemoteViews crashing SystemUi - - Catch canvas drawing exceptions caused by unsuported image sizes. - -Test: 1. Post a custom view notification with a layout - containing an ImageView that references a 5k x 5k image -2. Add an App Widget to the home screen with that has the - layout mentioned above as preview/initial layout. - -Bug: 268193777 -(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:10752edb540a053e304139894f941fcaef60949b) -Merged-In: Ib3bda769c499b4069b49c566b1b227f98f707a8a -Change-Id: Ib3bda769c499b4069b49c566b1b227f98f707a8a ---- - .../android/appwidget/AppWidgetHostView.java | 39 ++++++++++---- - .../row/ExpandableNotificationRow.java | 7 ++- - .../ExpandableNotificationRowController.java | 9 +++- - .../row/NotificationContentView.java | 54 ++++++++++++++++++- - .../row/NotificationTestHelper.java | 4 +- - 5 files changed, 97 insertions(+), 16 deletions(-) - -diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java -index fe10b7f8b3f4..27f6a266597c 100644 ---- a/core/java/android/appwidget/AppWidgetHostView.java -+++ b/core/java/android/appwidget/AppWidgetHostView.java -@@ -31,6 +31,7 @@ import android.content.pm.LauncherActivityInfo; - import android.content.pm.LauncherApps; - import android.content.pm.PackageManager.NameNotFoundException; - import android.content.res.Resources; -+import android.graphics.Canvas; - import android.graphics.Color; - import android.graphics.PointF; - import android.graphics.Rect; -@@ -311,19 +312,26 @@ public class AppWidgetHostView extends FrameLayout implements AppWidgetHost.AppW - super.onLayout(changed, left, top, right, bottom); - } catch (final RuntimeException e) { - Log.e(TAG, "Remote provider threw runtime exception, using error view instead.", e); -- removeViewInLayout(mView); -- View child = getErrorView(); -- prepareView(child); -- addViewInLayout(child, 0, child.getLayoutParams()); -- measureChild(child, MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY), -- MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY)); -- child.layout(0, 0, child.getMeasuredWidth() + mPaddingLeft + mPaddingRight, -- child.getMeasuredHeight() + mPaddingTop + mPaddingBottom); -- mView = child; -- mViewMode = VIEW_MODE_ERROR; -+ handleViewError(); - } - } - -+ /** -+ * Remove bad view and replace with error message view -+ */ -+ private void handleViewError() { -+ removeViewInLayout(mView); -+ View child = getErrorView(); -+ prepareView(child); -+ addViewInLayout(child, 0, child.getLayoutParams()); -+ measureChild(child, MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY), -+ MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY)); -+ child.layout(0, 0, child.getMeasuredWidth() + mPaddingLeft + mPaddingRight, -+ child.getMeasuredHeight() + mPaddingTop + mPaddingBottom); -+ mView = child; -+ mViewMode = VIEW_MODE_ERROR; -+ } -+ - /** - * Provide guidance about the size of this widget to the AppWidgetManager. The widths and - * heights should correspond to the full area the AppWidgetHostView is given. Padding added by -@@ -953,4 +961,15 @@ public class AppWidgetHostView extends FrameLayout implements AppWidgetHost.AppW - reapplyLastRemoteViews(); - } - } -+ -+ @Override -+ protected void dispatchDraw(@NonNull Canvas canvas) { -+ try { -+ super.dispatchDraw(canvas); -+ } catch (Exception e) { -+ // Catch draw exceptions that may be caused by RemoteViews -+ Log.e(TAG, "Drawing view failed: " + e); -+ post(this::handleViewError); -+ } -+ } - } -diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java -index 9f50aef6de11..816ccee604d4 100644 ---- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java -+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java -@@ -73,6 +73,7 @@ import androidx.annotation.Nullable; - import com.android.internal.annotations.VisibleForTesting; - import com.android.internal.logging.MetricsLogger; - import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -+import com.android.internal.statusbar.IStatusBarService; - import com.android.internal.util.ContrastColorUtil; - import com.android.internal.widget.CachingIconView; - import com.android.internal.widget.CallLayout; -@@ -1662,7 +1663,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView - NotificationGutsManager gutsManager, - MetricsLogger metricsLogger, - SmartReplyConstants smartReplyConstants, -- SmartReplyController smartReplyController) { -+ SmartReplyController smartReplyController, -+ IStatusBarService statusBarService) { - mEntry = entry; - mAppName = appName; - if (mMenuRow == null) { -@@ -1691,7 +1693,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView - mPeopleNotificationIdentifier, - rivSubcomponentFactory, - smartReplyConstants, -- smartReplyController); -+ smartReplyController, -+ statusBarService); - } - mOnUserInteractionCallback = onUserInteractionCallback; - mBubblesManagerOptional = bubblesManagerOptional; -diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java -index d1138608805b..a9bf51fae1fd 100644 ---- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java -+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java -@@ -29,6 +29,7 @@ import androidx.annotation.NonNull; - import androidx.annotation.Nullable; - - import com.android.internal.logging.MetricsLogger; -+import com.android.internal.statusbar.IStatusBarService; - import com.android.systemui.classifier.FalsingCollector; - import com.android.systemui.flags.FeatureFlags; - import com.android.systemui.flags.Flags; -@@ -125,6 +126,7 @@ public class ExpandableNotificationRowController implements NotifViewController - } - }; - -+ private final IStatusBarService mStatusBarService; - - @Inject - public ExpandableNotificationRowController( -@@ -157,7 +159,8 @@ public class ExpandableNotificationRowController implements NotifViewController - FeatureFlags featureFlags, - PeopleNotificationIdentifier peopleNotificationIdentifier, - Optional bubblesManagerOptional, -- ExpandableNotificationRowDragController dragController) { -+ ExpandableNotificationRowDragController dragController, -+ IStatusBarService statusBarService) { - mView = view; - mListContainer = listContainer; - mRemoteInputViewSubcomponentFactory = rivSubcomponentFactory; -@@ -189,6 +192,7 @@ public class ExpandableNotificationRowController implements NotifViewController - mLogBufferLogger = logBufferLogger; - mSmartReplyConstants = smartReplyConstants; - mSmartReplyController = smartReplyController; -+ mStatusBarService = statusBarService; - } - - /** -@@ -219,7 +223,8 @@ public class ExpandableNotificationRowController implements NotifViewController - mNotificationGutsManager, - mMetricsLogger, - mSmartReplyConstants, -- mSmartReplyController -+ mSmartReplyController, -+ mStatusBarService - ); - mView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); - if (mAllowLongPress) { -diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java -index e46bf522acff..6b729f240c80 100644 ---- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java -+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java -@@ -21,10 +21,13 @@ import android.annotation.Nullable; - import android.app.Notification; - import android.app.PendingIntent; - import android.content.Context; -+import android.graphics.Canvas; - import android.graphics.Rect; - import android.graphics.drawable.Drawable; - import android.os.Build; -+import android.os.RemoteException; - import android.provider.Settings; -+import android.service.notification.StatusBarNotification; - import android.util.ArrayMap; - import android.util.AttributeSet; - import android.util.IndentingPrintWriter; -@@ -39,6 +42,7 @@ import android.widget.ImageView; - import android.widget.LinearLayout; - - import com.android.internal.annotations.VisibleForTesting; -+import com.android.internal.statusbar.IStatusBarService; - import com.android.systemui.R; - import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; - import com.android.systemui.statusbar.RemoteInputController; -@@ -129,6 +133,7 @@ public class NotificationContentView extends FrameLayout implements Notification - private Runnable mExpandedVisibleListener; - private PeopleNotificationIdentifier mPeopleIdentifier; - private RemoteInputViewSubcomponent.Factory mRemoteInputSubcomponentFactory; -+ private IStatusBarService mStatusBarService; - - /** - * List of listeners for when content views become inactive (i.e. not the showing view). -@@ -194,11 +199,13 @@ public class NotificationContentView extends FrameLayout implements Notification - PeopleNotificationIdentifier peopleNotificationIdentifier, - RemoteInputViewSubcomponent.Factory rivSubcomponentFactory, - SmartReplyConstants smartReplyConstants, -- SmartReplyController smartReplyController) { -+ SmartReplyController smartReplyController, -+ IStatusBarService statusBarService) { - mPeopleIdentifier = peopleNotificationIdentifier; - mRemoteInputSubcomponentFactory = rivSubcomponentFactory; - mSmartReplyConstants = smartReplyConstants; - mSmartReplyController = smartReplyController; -+ mStatusBarService = statusBarService; - } - - public void reinflate() { -@@ -2133,4 +2140,49 @@ public class NotificationContentView extends FrameLayout implements Notification - @Nullable RemoteInputView mView; - @Nullable RemoteInputViewController mController; - } -+ -+ @VisibleForTesting -+ protected void setContractedWrapper(NotificationViewWrapper contractedWrapper) { -+ mContractedWrapper = contractedWrapper; -+ } -+ @VisibleForTesting -+ protected void setExpandedWrapper(NotificationViewWrapper expandedWrapper) { -+ mExpandedWrapper = expandedWrapper; -+ } -+ @VisibleForTesting -+ protected void setHeadsUpWrapper(NotificationViewWrapper headsUpWrapper) { -+ mHeadsUpWrapper = headsUpWrapper; -+ } -+ -+ @Override -+ protected void dispatchDraw(Canvas canvas) { -+ try { -+ super.dispatchDraw(canvas); -+ } catch (Exception e) { -+ // Catch draw exceptions that may be caused by RemoteViews -+ Log.e(TAG, "Drawing view failed: " + e); -+ cancelNotification(e); -+ } -+ } -+ -+ private void cancelNotification(Exception exception) { -+ try { -+ setVisibility(GONE); -+ final StatusBarNotification sbn = mNotificationEntry.getSbn(); -+ if (mStatusBarService != null) { -+ // report notification inflation errors back up -+ // to notification delegates -+ mStatusBarService.onNotificationError( -+ sbn.getPackageName(), -+ sbn.getTag(), -+ sbn.getId(), -+ sbn.getUid(), -+ sbn.getInitialPid(), -+ exception.getMessage(), -+ sbn.getUser().getIdentifier()); -+ } -+ } catch (RemoteException ex) { -+ Log.e(TAG, "cancelNotification failed: " + ex); -+ } -+ } - } -diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java -index 728e0265c729..e8aeb18552f8 100644 ---- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java -+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java -@@ -50,6 +50,7 @@ import android.widget.RemoteViews; - - import com.android.internal.logging.MetricsLogger; - import com.android.internal.logging.UiEventLogger; -+import com.android.internal.statusbar.IStatusBarService; - import com.android.systemui.TestableDependency; - import com.android.systemui.classifier.FalsingCollectorFake; - import com.android.systemui.classifier.FalsingManagerFake; -@@ -561,7 +562,8 @@ public class NotificationTestHelper { - mock(NotificationGutsManager.class), - mock(MetricsLogger.class), - mock(SmartReplyConstants.class), -- mock(SmartReplyController.class)); -+ mock(SmartReplyController.class), -+ mock(IStatusBarService.class)); - - row.setAboveShelfChangedListener(aboveShelf -> { }); - mBindStage.getStageParams(entry).requireContentViews(extraInflationFlags); diff --git a/Patches/LineageOS-20.0/android_frameworks_base/ASB-2023-06/0005-Check-key-intent-for-selectors-and-prohibited-flags.patch b/Patches/LineageOS-20.0/android_frameworks_base/ASB-2023-06/0005-Check-key-intent-for-selectors-and-prohibited-flags.patch deleted file mode 100644 index 06e5bfe8..00000000 --- a/Patches/LineageOS-20.0/android_frameworks_base/ASB-2023-06/0005-Check-key-intent-for-selectors-and-prohibited-flags.patch +++ /dev/null @@ -1,167 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Brian Lee -Date: Fri, 17 Feb 2023 16:05:17 -0800 -Subject: [PATCH 05/10] Check key intent for selectors and prohibited flags - -Bug: 265015796 -Test: atest -FrameworksServicesTests: com.android.server.accounts.AccountManagerServiceTest -(cherry picked from commit e53a96304352e2965176c8d32ac1b504e52ef185) -(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:64f6c1e13588af3cf4d88a39d9d540c140982043) -Merged-In: Ie16f8654337bd75eaad3156817470674b4f0cee3 -Change-Id: Ie16f8654337bd75eaad3156817470674b4f0cee3 ---- - .../accounts/AccountManagerService.java | 18 +++++++--- - .../accounts/AccountManagerServiceTest.java | 36 +++++++++++++++++++ - .../AccountManagerServiceTestFixtures.java | 5 ++- - .../TestAccountType1Authenticator.java | 5 +-- - 4 files changed, 54 insertions(+), 10 deletions(-) - -diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java -index 425158195940..1dc0942ceac5 100644 ---- a/services/core/java/com/android/server/accounts/AccountManagerService.java -+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java -@@ -4893,10 +4893,6 @@ public class AccountManagerService - if (intent.getClipData() == null) { - intent.setClipData(ClipData.newPlainText(null, null)); - } -- intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_READ_URI_PERMISSION -- | Intent.FLAG_GRANT_WRITE_URI_PERMISSION -- | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION -- | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION)); - final long bid = Binder.clearCallingIdentity(); - try { - PackageManager pm = mContext.getPackageManager(); -@@ -4942,7 +4938,19 @@ public class AccountManagerService - if (intent == null) { - return (simulateIntent == null); - } -- return intent.filterEquals(simulateIntent); -+ if (!intent.filterEquals(simulateIntent)) { -+ return false; -+ } -+ -+ if (intent.getSelector() != simulateIntent.getSelector()) { -+ return false; -+ } -+ -+ int prohibitedFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION -+ | Intent.FLAG_GRANT_WRITE_URI_PERMISSION -+ | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION -+ | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION; -+ return (simulateIntent.getFlags() & prohibitedFlags) == 0; - } - - private boolean isExportedSystemActivity(ActivityInfo activityInfo) { -diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java -index 30ec1632a622..881d1b3d581c 100644 ---- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java -+++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java -@@ -18,6 +18,7 @@ package com.android.server.accounts; - - import static android.database.sqlite.SQLiteDatabase.deleteDatabase; - -+import static org.mockito.ArgumentMatchers.contains; - import static org.mockito.Matchers.any; - import static org.mockito.Matchers.anyBoolean; - import static org.mockito.Matchers.anyInt; -@@ -707,6 +708,41 @@ public class AccountManagerServiceTest extends AndroidTestCase { - assertNotNull(intent.getParcelableExtra(AccountManagerServiceTestFixtures.KEY_CALLBACK)); - } - -+ @SmallTest -+ public void testStartAddAccountSessionWhereAuthenticatorReturnsIntentWithProhibitedFlags() -+ throws Exception { -+ unlockSystemUser(); -+ ResolveInfo resolveInfo = new ResolveInfo(); -+ resolveInfo.activityInfo = new ActivityInfo(); -+ resolveInfo.activityInfo.applicationInfo = new ApplicationInfo(); -+ when(mMockPackageManager.resolveActivityAsUser( -+ any(Intent.class), anyInt(), anyInt())).thenReturn(resolveInfo); -+ when(mMockPackageManager.checkSignatures( -+ anyInt(), anyInt())).thenReturn(PackageManager.SIGNATURE_MATCH); -+ -+ final CountDownLatch latch = new CountDownLatch(1); -+ Response response = new Response(latch, mMockAccountManagerResponse); -+ Bundle options = createOptionsWithAccountName( -+ AccountManagerServiceTestFixtures.ACCOUNT_NAME_INTERVENE); -+ int prohibitedFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION -+ | Intent.FLAG_GRANT_WRITE_URI_PERMISSION -+ | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION -+ | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION; -+ options.putInt(AccountManagerServiceTestFixtures.KEY_INTENT_FLAGS, prohibitedFlags); -+ -+ mAms.startAddAccountSession( -+ response, // response -+ AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, // accountType -+ "authTokenType", -+ null, // requiredFeatures -+ true, // expectActivityLaunch -+ options); // optionsIn -+ waitForLatch(latch); -+ -+ verify(mMockAccountManagerResponse).onError( -+ eq(AccountManager.ERROR_CODE_INVALID_RESPONSE), contains("invalid intent")); -+ } -+ - @SmallTest - public void testStartAddAccountSessionError() throws Exception { - unlockSystemUser(); -diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java -index 73f30d9f9e79..b98a6a891d55 100644 ---- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java -+++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java -@@ -17,9 +17,6 @@ package com.android.server.accounts; - - import android.accounts.Account; - --import java.util.ArrayList; --import java.util.List; -- - /** - * Constants shared between test AccountAuthenticators and AccountManagerServiceTest. - */ -@@ -31,6 +28,8 @@ public final class AccountManagerServiceTestFixtures { - "account_manager_service_test:account_status_token_key"; - public static final String KEY_ACCOUNT_PASSWORD = - "account_manager_service_test:account_password_key"; -+ public static final String KEY_INTENT_FLAGS = -+ "account_manager_service_test:intent_flags_key"; - public static final String KEY_OPTIONS_BUNDLE = - "account_manager_service_test:option_bundle_key"; - public static final String ACCOUNT_NAME_SUCCESS = "success_on_return@fixture.com"; -diff --git a/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java b/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java -index 8106364477d9..924443e9d5cf 100644 ---- a/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java -+++ b/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java -@@ -24,8 +24,6 @@ import android.content.Context; - import android.content.Intent; - import android.os.Bundle; - --import com.android.frameworks.servicestests.R; -- - import java.util.concurrent.atomic.AtomicInteger; - - /** -@@ -270,11 +268,13 @@ public class TestAccountType1Authenticator extends AbstractAccountAuthenticator - String accountName = null; - Bundle sessionBundle = null; - String password = null; -+ int intentFlags = 0; - if (options != null) { - accountName = options.getString(AccountManagerServiceTestFixtures.KEY_ACCOUNT_NAME); - sessionBundle = options.getBundle( - AccountManagerServiceTestFixtures.KEY_ACCOUNT_SESSION_BUNDLE); - password = options.getString(AccountManagerServiceTestFixtures.KEY_ACCOUNT_PASSWORD); -+ intentFlags = options.getInt(AccountManagerServiceTestFixtures.KEY_INTENT_FLAGS, 0); - } - - Bundle result = new Bundle(); -@@ -302,6 +302,7 @@ public class TestAccountType1Authenticator extends AbstractAccountAuthenticator - intent.putExtra(AccountManagerServiceTestFixtures.KEY_RESULT, - eventualActivityResultData); - intent.putExtra(AccountManagerServiceTestFixtures.KEY_CALLBACK, response); -+ intent.setFlags(intentFlags); - - result.putParcelable(AccountManager.KEY_INTENT, intent); - } else { diff --git a/Patches/LineageOS-20.0/android_frameworks_base/ASB-2023-06/0006-Use-PendingIntent-for-media-click-action-over-locksc.patch b/Patches/LineageOS-20.0/android_frameworks_base/ASB-2023-06/0006-Use-PendingIntent-for-media-click-action-over-locksc.patch deleted file mode 100644 index 715e7c27..00000000 --- a/Patches/LineageOS-20.0/android_frameworks_base/ASB-2023-06/0006-Use-PendingIntent-for-media-click-action-over-locksc.patch +++ /dev/null @@ -1,48 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Beth Thibodeau -Date: Mon, 13 Mar 2023 16:59:33 -0500 -Subject: [PATCH 06/10] Use PendingIntent for media click action over - lockscreen - -The clickIntent is provided by apps as the notification's contentIntent, -and it should be sent as is. This fixes the case where the intent called -an activity that could show over lockscreen. - -Bug: 271845008 -Test: atest MediaControlPanelTest -Test: manually with test app -(cherry picked from commit cb2904c7ff653a87cc98904bcb3bcb9c3b6e06ea) -(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:223e9c5839308d8cd2e14242315a0e27a5154258) -Merged-In: I09d64452c46c4d21b9d958570020b2f5e6c2b23f -Change-Id: I09d64452c46c4d21b9d958570020b2f5e6c2b23f ---- - .../media/controls/ui/MediaControlPanel.java | 12 ++++++------ - 1 file changed, 6 insertions(+), 6 deletions(-) - -diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java -index 15c34430f455..5cdfe73ad3aa 100644 ---- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java -+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java -@@ -495,16 +495,16 @@ public class MediaControlPanel { - mLogger.logTapContentView(mUid, mPackageName, mInstanceId); - logSmartspaceCardReported(SMARTSPACE_CARD_CLICK_EVENT); - -- // See StatusBarNotificationActivityStarter#onNotificationClicked - boolean showOverLockscreen = mKeyguardStateController.isShowing() -- && mActivityIntentHelper.wouldShowOverLockscreen(clickIntent.getIntent(), -+ && mActivityIntentHelper.wouldPendingShowOverLockscreen(clickIntent, - mLockscreenUserManager.getCurrentUserId()); - - if (showOverLockscreen) { -- mActivityStarter.startActivity(clickIntent.getIntent(), -- /* dismissShade */ true, -- /* animationController */ null, -- /* showOverLockscreenWhenLocked */ true); -+ try { -+ clickIntent.send(); -+ } catch (PendingIntent.CanceledException e) { -+ Log.e(TAG, "Pending intent for " + key + " was cancelled"); -+ } - } else { - mActivityStarter.postStartActivityDismissingKeyguard(clickIntent, - buildLaunchAnimatorController(mMediaViewHolder.getPlayer())); diff --git a/Patches/LineageOS-20.0/android_frameworks_base/ASB-2023-06/0007-Allow-filtering-of-services.patch b/Patches/LineageOS-20.0/android_frameworks_base/ASB-2023-06/0007-Allow-filtering-of-services.patch deleted file mode 100644 index d75ef09f..00000000 --- a/Patches/LineageOS-20.0/android_frameworks_base/ASB-2023-06/0007-Allow-filtering-of-services.patch +++ /dev/null @@ -1,236 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Julia Reynolds -Date: Tue, 7 Mar 2023 15:44:49 -0500 -Subject: [PATCH 07/10] Allow filtering of services - -Test: ServiceListingTest -Bug: 260570119 -(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:ad19ca301191bc709b386bb10f4337cacd895b9e) -Merged-In: Ib4740ba401667de62fa1a33334c2c1fbee25b760 -Change-Id: Ib4740ba401667de62fa1a33334c2c1fbee25b760 ---- - .../applications/ServiceListing.java | 17 +++- - .../applications/ServiceListingTest.java | 98 ++++++++++++++++++- - 2 files changed, 111 insertions(+), 4 deletions(-) - -diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ServiceListing.java b/packages/SettingsLib/src/com/android/settingslib/applications/ServiceListing.java -index bd9e760acfda..c8bcabff1094 100644 ---- a/packages/SettingsLib/src/com/android/settingslib/applications/ServiceListing.java -+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ServiceListing.java -@@ -35,6 +35,7 @@ import android.util.Slog; - import java.util.ArrayList; - import java.util.HashSet; - import java.util.List; -+import java.util.function.Predicate; - - /** - * Class for managing services matching a given intent and requesting a given permission. -@@ -51,12 +52,13 @@ public class ServiceListing { - private final HashSet mEnabledServices = new HashSet<>(); - private final List mServices = new ArrayList<>(); - private final List mCallbacks = new ArrayList<>(); -+ private final Predicate mValidator; - - private boolean mListening; - - private ServiceListing(Context context, String tag, - String setting, String intentAction, String permission, String noun, -- boolean addDeviceLockedFlags) { -+ boolean addDeviceLockedFlags, Predicate validator) { - mContentResolver = context.getContentResolver(); - mContext = context; - mTag = tag; -@@ -65,6 +67,7 @@ public class ServiceListing { - mPermission = permission; - mNoun = noun; - mAddDeviceLockedFlags = addDeviceLockedFlags; -+ mValidator = validator; - } - - public void addCallback(Callback callback) { -@@ -137,7 +140,6 @@ public class ServiceListing { - final PackageManager pmWrapper = mContext.getPackageManager(); - List installedServices = pmWrapper.queryIntentServicesAsUser( - new Intent(mIntentAction), flags, user); -- - for (ResolveInfo resolveInfo : installedServices) { - ServiceInfo info = resolveInfo.serviceInfo; - -@@ -148,6 +150,9 @@ public class ServiceListing { - + mPermission); - continue; - } -+ if (mValidator != null && !mValidator.test(info)) { -+ continue; -+ } - mServices.add(info); - } - for (Callback callback : mCallbacks) { -@@ -194,6 +199,7 @@ public class ServiceListing { - private String mPermission; - private String mNoun; - private boolean mAddDeviceLockedFlags = false; -+ private Predicate mValidator; - - public Builder(Context context) { - mContext = context; -@@ -224,6 +230,11 @@ public class ServiceListing { - return this; - } - -+ public Builder setValidator(Predicate validator) { -+ mValidator = validator; -+ return this; -+ } -+ - /** - * Set to true to add support for both MATCH_DIRECT_BOOT_AWARE and - * MATCH_DIRECT_BOOT_UNAWARE flags when querying PackageManager. Required to get results -@@ -236,7 +247,7 @@ public class ServiceListing { - - public ServiceListing build() { - return new ServiceListing(mContext, mTag, mSetting, mIntentAction, mPermission, mNoun, -- mAddDeviceLockedFlags); -+ mAddDeviceLockedFlags, mValidator); - } - } - } -diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ServiceListingTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ServiceListingTest.java -index f7fd25b9fb7d..7ff0988c494d 100644 ---- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ServiceListingTest.java -+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ServiceListingTest.java -@@ -18,20 +18,35 @@ package com.android.settingslib.applications; - - import static com.google.common.truth.Truth.assertThat; - -+import static org.mockito.ArgumentMatchers.any; -+import static org.mockito.ArgumentMatchers.anyInt; - import static org.mockito.ArgumentMatchers.anyList; - import static org.mockito.Mockito.mock; -+import static org.mockito.Mockito.spy; - import static org.mockito.Mockito.times; - import static org.mockito.Mockito.verify; -+import static org.mockito.Mockito.when; - - import android.content.ComponentName; -+import android.content.Context; -+import android.content.pm.PackageManager; -+import android.content.pm.ResolveInfo; -+import android.content.pm.ServiceInfo; - import android.provider.Settings; - -+import androidx.test.core.app.ApplicationProvider; -+ -+import com.google.common.collect.ImmutableList; -+ - import org.junit.Before; - import org.junit.Test; - import org.junit.runner.RunWith; -+import org.mockito.ArgumentCaptor; - import org.robolectric.RobolectricTestRunner; - import org.robolectric.RuntimeEnvironment; - -+import java.util.List; -+ - @RunWith(RobolectricTestRunner.class) - public class ServiceListingTest { - -@@ -39,16 +54,97 @@ public class ServiceListingTest { - private static final String TEST_INTENT = "com.example.intent"; - - private ServiceListing mServiceListing; -+ private Context mContext; -+ private PackageManager mPm; - - @Before - public void setUp() { -- mServiceListing = new ServiceListing.Builder(RuntimeEnvironment.application) -+ mPm = mock(PackageManager.class); -+ mContext = spy(ApplicationProvider.getApplicationContext()); -+ when(mContext.getPackageManager()).thenReturn(mPm); -+ -+ mServiceListing = new ServiceListing.Builder(mContext) -+ .setTag("testTag") -+ .setSetting(TEST_SETTING) -+ .setNoun("testNoun") -+ .setIntentAction(TEST_INTENT) -+ .setPermission("testPermission") -+ .build(); -+ } -+ -+ @Test -+ public void testValidator() { -+ ServiceInfo s1 = new ServiceInfo(); -+ s1.permission = "testPermission"; -+ s1.packageName = "pkg"; -+ ServiceInfo s2 = new ServiceInfo(); -+ s2.permission = "testPermission"; -+ s2.packageName = "pkg2"; -+ ResolveInfo r1 = new ResolveInfo(); -+ r1.serviceInfo = s1; -+ ResolveInfo r2 = new ResolveInfo(); -+ r2.serviceInfo = s2; -+ -+ when(mPm.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn( -+ ImmutableList.of(r1, r2)); -+ -+ mServiceListing = new ServiceListing.Builder(mContext) -+ .setTag("testTag") -+ .setSetting(TEST_SETTING) -+ .setNoun("testNoun") -+ .setIntentAction(TEST_INTENT) -+ .setValidator(info -> { -+ if (info.packageName.equals("pkg")) { -+ return true; -+ } -+ return false; -+ }) -+ .setPermission("testPermission") -+ .build(); -+ ServiceListing.Callback callback = mock(ServiceListing.Callback.class); -+ mServiceListing.addCallback(callback); -+ mServiceListing.reload(); -+ -+ verify(mPm).queryIntentServicesAsUser(any(), anyInt(), anyInt()); -+ ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); -+ verify(callback, times(1)).onServicesReloaded(captor.capture()); -+ -+ assertThat(captor.getValue().size()).isEqualTo(1); -+ assertThat(captor.getValue().get(0)).isEqualTo(s1); -+ } -+ -+ @Test -+ public void testNoValidator() { -+ ServiceInfo s1 = new ServiceInfo(); -+ s1.permission = "testPermission"; -+ s1.packageName = "pkg"; -+ ServiceInfo s2 = new ServiceInfo(); -+ s2.permission = "testPermission"; -+ s2.packageName = "pkg2"; -+ ResolveInfo r1 = new ResolveInfo(); -+ r1.serviceInfo = s1; -+ ResolveInfo r2 = new ResolveInfo(); -+ r2.serviceInfo = s2; -+ -+ when(mPm.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn( -+ ImmutableList.of(r1, r2)); -+ -+ mServiceListing = new ServiceListing.Builder(mContext) - .setTag("testTag") - .setSetting(TEST_SETTING) - .setNoun("testNoun") - .setIntentAction(TEST_INTENT) - .setPermission("testPermission") - .build(); -+ ServiceListing.Callback callback = mock(ServiceListing.Callback.class); -+ mServiceListing.addCallback(callback); -+ mServiceListing.reload(); -+ -+ verify(mPm).queryIntentServicesAsUser(any(), anyInt(), anyInt()); -+ ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); -+ verify(callback, times(1)).onServicesReloaded(captor.capture()); -+ -+ assertThat(captor.getValue().size()).isEqualTo(2); - } - - @Test diff --git a/Patches/LineageOS-20.0/android_frameworks_base/ASB-2023-06/0008-Enforce-DevicePolicyManager.setUserControlDisabledPa.patch b/Patches/LineageOS-20.0/android_frameworks_base/ASB-2023-06/0008-Enforce-DevicePolicyManager.setUserControlDisabledPa.patch deleted file mode 100644 index 750e150b..00000000 --- a/Patches/LineageOS-20.0/android_frameworks_base/ASB-2023-06/0008-Enforce-DevicePolicyManager.setUserControlDisabledPa.patch +++ /dev/null @@ -1,252 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Alex Johnston -Date: Wed, 8 Mar 2023 22:28:28 +0000 -Subject: [PATCH 08/10] Enforce - DevicePolicyManager.setUserControlDisabledPackages in AppStandbyController - -When deciding an app's standby bucket, check if the -app has its user control disabled by an IT admin. If so, -the app should be the exempted restricted bucket. - -Bug: 272042183 -Test: atest AppStandbyControllerTests -(cherry picked from commit 269fcb6873dee199dd8023831f882aafff1f6291) -(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:99b199d1139f50dbecba4f4bdc8066c6d0c28b5b) -Merged-In: I4279dc37f0e17aedb1c2a87468478248443a253e -Change-Id: I4279dc37f0e17aedb1c2a87468478248443a253e ---- - .../server/usage/AppStandbyInternal.java | 2 + - .../server/usage/AppStandbyController.java | 40 +++++++++++++++++++ - .../app/admin/DevicePolicyManager.java | 3 +- - .../app/usage/UsageStatsManagerInternal.java | 10 +++++ - .../DevicePolicyManagerService.java | 2 + - .../usage/AppStandbyControllerTests.java | 38 ++++++++++++++++++ - .../server/usage/UsageStatsService.java | 5 +++ - 7 files changed, 99 insertions(+), 1 deletion(-) - -diff --git a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java -index 9b64edf53d8c..f50a90248030 100644 ---- a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java -+++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java -@@ -225,6 +225,8 @@ public interface AppStandbyInternal { - - void setActiveAdminApps(Set adminPkgs, int userId); - -+ void setAdminProtectedPackages(Set packageNames, int userId); -+ - /** - * @return {@code true} if the given package is an active device admin app. - */ -diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java -index a6f47d4e4908..b27ff411dd58 100644 ---- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java -+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java -@@ -264,6 +264,10 @@ public class AppStandbyController - @GuardedBy("mActiveAdminApps") - private final SparseArray> mActiveAdminApps = new SparseArray<>(); - -+ /** List of admin protected packages. Can contain {@link android.os.UserHandle#USER_ALL}. */ -+ @GuardedBy("mAdminProtectedPackages") -+ private final SparseArray> mAdminProtectedPackages = new SparseArray<>(); -+ - /** - * Set of system apps that are headless (don't have any "front door" activities, enabled or - * disabled). Presence in this map indicates that the app is a headless system app. -@@ -1335,6 +1339,9 @@ public class AppStandbyController - synchronized (mActiveAdminApps) { - mActiveAdminApps.remove(userId); - } -+ synchronized (mAdminProtectedPackages) { -+ mAdminProtectedPackages.remove(userId); -+ } - } - } - -@@ -1424,6 +1431,10 @@ public class AppStandbyController - return STANDBY_BUCKET_EXEMPTED; - } - -+ if (isAdminProtectedPackages(packageName, userId)) { -+ return STANDBY_BUCKET_EXEMPTED; -+ } -+ - if (isActiveNetworkScorer(packageName)) { - return STANDBY_BUCKET_EXEMPTED; - } -@@ -1871,6 +1882,17 @@ public class AppStandbyController - } - } - -+ private boolean isAdminProtectedPackages(String packageName, int userId) { -+ synchronized (mAdminProtectedPackages) { -+ if (mAdminProtectedPackages.contains(UserHandle.USER_ALL) -+ && mAdminProtectedPackages.get(UserHandle.USER_ALL).contains(packageName)) { -+ return true; -+ } -+ return mAdminProtectedPackages.contains(userId) -+ && mAdminProtectedPackages.get(userId).contains(packageName); -+ } -+ } -+ - @Override - public void addActiveDeviceAdmin(String adminPkg, int userId) { - synchronized (mActiveAdminApps) { -@@ -1894,6 +1916,17 @@ public class AppStandbyController - } - } - -+ @Override -+ public void setAdminProtectedPackages(Set packageNames, int userId) { -+ synchronized (mAdminProtectedPackages) { -+ if (packageNames == null || packageNames.isEmpty()) { -+ mAdminProtectedPackages.remove(userId); -+ } else { -+ mAdminProtectedPackages.put(userId, packageNames); -+ } -+ } -+ } -+ - @Override - public void onAdminDataAvailable() { - mAdminDataAvailableLatch.countDown(); -@@ -1916,6 +1949,13 @@ public class AppStandbyController - } - } - -+ @VisibleForTesting -+ Set getAdminProtectedPackagesForTest(int userId) { -+ synchronized (mAdminProtectedPackages) { -+ return mAdminProtectedPackages.get(userId); -+ } -+ } -+ - /** - * Returns {@code true} if the supplied package is the device provisioning app. Otherwise, - * returns {@code false}. -diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java -index f563fdb28953..af42dd3dfc76 100644 ---- a/core/java/android/app/admin/DevicePolicyManager.java -+++ b/core/java/android/app/admin/DevicePolicyManager.java -@@ -14625,7 +14625,8 @@ public class DevicePolicyManager { - /** - * Called by a device owner or a profile owner to disable user control over apps. User will not - * be able to clear app data or force-stop packages. When called by a device owner, applies to -- * all users on the device. -+ * all users on the device. Packages with user control disabled are exempted from -+ * App Standby Buckets. - * - * @param admin which {@link DeviceAdminReceiver} this request is associated with - * @param packages The package names for the apps. -diff --git a/services/core/java/android/app/usage/UsageStatsManagerInternal.java b/services/core/java/android/app/usage/UsageStatsManagerInternal.java -index a35aa7c74ee5..ee70e2ff50ce 100644 ---- a/services/core/java/android/app/usage/UsageStatsManagerInternal.java -+++ b/services/core/java/android/app/usage/UsageStatsManagerInternal.java -@@ -202,6 +202,16 @@ public abstract class UsageStatsManagerInternal { - */ - public abstract void setActiveAdminApps(Set adminApps, int userId); - -+ /** -+ * Called by DevicePolicyManagerService to inform about the protected packages for a user. -+ * User control will be disabled for protected packages. -+ * -+ * @param packageNames the set of protected packages for {@code userId}. -+ * @param userId the userId to which the protected packages belong. -+ */ -+ public abstract void setAdminProtectedPackages(@Nullable Set packageNames, -+ @UserIdInt int userId); -+ - /** - * Called by DevicePolicyManagerService during boot to inform that admin data is loaded and - * pushed to UsageStatsService. -diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java -index 515d6dc9cab1..e95f827ff6f1 100644 ---- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java -+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java -@@ -3202,6 +3202,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { - mInjector.binderWithCleanCallingIdentity(() -> - mInjector.getPackageManagerInternal().setOwnerProtectedPackages( - targetUserId, protectedPackages)); -+ mUsageStatsManagerInternal.setAdminProtectedPackages(new ArraySet(protectedPackages), -+ targetUserId); - } - - @Override -diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java -index 308a4b67de24..e2db7584a2a6 100644 ---- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java -+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java -@@ -158,6 +158,9 @@ public class AppStandbyControllerTests { - private static final String ADMIN_PKG2 = "com.android.admin2"; - private static final String ADMIN_PKG3 = "com.android.admin3"; - -+ private static final String ADMIN_PROTECTED_PKG = "com.android.admin.protected"; -+ private static final String ADMIN_PROTECTED_PKG2 = "com.android.admin.protected2"; -+ - private static final long MINUTE_MS = 60 * 1000; - private static final long HOUR_MS = 60 * MINUTE_MS; - private static final long DAY_MS = 24 * HOUR_MS; -@@ -1750,6 +1753,19 @@ public class AppStandbyControllerTests { - assertIsNotActiveAdmin(ADMIN_PKG2, USER_ID); - } - -+ @Test -+ public void testSetAdminProtectedPackages() { -+ assertAdminProtectedPackagesForTest(USER_ID, (String[]) null); -+ assertAdminProtectedPackagesForTest(USER_ID2, (String[]) null); -+ -+ setAdminProtectedPackages(USER_ID, ADMIN_PROTECTED_PKG, ADMIN_PROTECTED_PKG2); -+ assertAdminProtectedPackagesForTest(USER_ID, ADMIN_PROTECTED_PKG, ADMIN_PROTECTED_PKG2); -+ assertAdminProtectedPackagesForTest(USER_ID2, (String[]) null); -+ -+ setAdminProtectedPackages(USER_ID, (String[]) null); -+ assertAdminProtectedPackagesForTest(USER_ID, (String[]) null); -+ } -+ - @Test - @FlakyTest(bugId = 185169504) - public void testUserInteraction_CrossProfile() throws Exception { -@@ -2144,6 +2160,28 @@ public class AppStandbyControllerTests { - mController.setActiveAdminApps(new ArraySet<>(Arrays.asList(admins)), userId); - } - -+ private void setAdminProtectedPackages(int userId, String... packageNames) { -+ Set adminProtectedPackages = packageNames != null ? new ArraySet<>( -+ Arrays.asList(packageNames)) : null; -+ mController.setAdminProtectedPackages(adminProtectedPackages, userId); -+ } -+ -+ private void assertAdminProtectedPackagesForTest(int userId, String... packageNames) { -+ final Set actualAdminProtectedPackages = -+ mController.getAdminProtectedPackagesForTest(userId); -+ if (packageNames == null) { -+ if (actualAdminProtectedPackages != null && !actualAdminProtectedPackages.isEmpty()) { -+ fail("Admin protected packages should be null; " + getAdminAppsStr(userId, -+ actualAdminProtectedPackages)); -+ } -+ return; -+ } -+ assertEquals(packageNames.length, actualAdminProtectedPackages.size()); -+ for (String adminProtectedPackage : packageNames) { -+ assertTrue(actualAdminProtectedPackages.contains(adminProtectedPackage)); -+ } -+ } -+ - private void setAndAssertBucket(String pkg, int user, int bucket, int reason) throws Exception { - rearmLatch(pkg); - mController.setAppStandbyBucket(pkg, user, bucket, reason); -diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java -index ea40100227c4..71644d08e720 100644 ---- a/services/usage/java/com/android/server/usage/UsageStatsService.java -+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java -@@ -3086,6 +3086,11 @@ public class UsageStatsService extends SystemService implements - mAppStandby.setActiveAdminApps(packageNames, userId); - } - -+ @Override -+ public void setAdminProtectedPackages(Set packageNames, int userId) { -+ mAppStandby.setAdminProtectedPackages(packageNames, userId); -+ } -+ - @Override - public void onAdminDataAvailable() { - mAppStandby.onAdminDataAvailable(); diff --git a/Patches/LineageOS-20.0/android_frameworks_base/ASB-2023-06/0009-Add-BubbleMetadata-detection-to-block-FSI.patch b/Patches/LineageOS-20.0/android_frameworks_base/ASB-2023-06/0009-Add-BubbleMetadata-detection-to-block-FSI.patch deleted file mode 100644 index 72c50a4c..00000000 --- a/Patches/LineageOS-20.0/android_frameworks_base/ASB-2023-06/0009-Add-BubbleMetadata-detection-to-block-FSI.patch +++ /dev/null @@ -1,72 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Jeff DeCew -Date: Fri, 24 Mar 2023 16:15:24 +0000 -Subject: [PATCH 09/10] Add BubbleMetadata detection to block FSI - -Bug: 274759612 -Test: atest NotificationInterruptStateProviderImplTest -(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:e65f0c9643b52e2656ac2da21dfd0fb7395de04c) -Merged-In: I40e1aa6377b8a60d91cb2f4189df1e9a4a4578a2 -Change-Id: I40e1aa6377b8a60d91cb2f4189df1e9a4a4578a2 ---- - ...otificationInterruptStateProviderImpl.java | 11 ++++++++++ - ...icationInterruptStateProviderImplTest.java | 21 +++++++++++++++++++ - 2 files changed, 32 insertions(+) - -diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java -index d9dacfd0e27c..5956c5473843 100644 ---- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java -+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java -@@ -266,6 +266,17 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter - suppressedByDND); - } - -+ // If the notification has suppressive BubbleMetadata, block FSI and warn. -+ Notification.BubbleMetadata bubbleMetadata = sbn.getNotification().getBubbleMetadata(); -+ if (bubbleMetadata != null && bubbleMetadata.isNotificationSuppressed()) { -+ // b/274759612: Detect and report an event when a notification has both an FSI and a -+ // suppressive BubbleMetadata, and now correctly block the FSI from firing. -+ final int uid = entry.getSbn().getUid(); -+ android.util.EventLog.writeEvent(0x534e4554, "274759612", uid, "bubbleMetadata"); -+ mLogger.logNoFullscreenWarning(entry, "BubbleMetadata may prevent HUN"); -+ return FullScreenIntentDecision.NO_FULL_SCREEN_INTENT; -+ } -+ - // If the screen is off, then launch the FullScreenIntent - if (!mPowerManager.isInteractive()) { - return getDecisionGivenSuppression(FullScreenIntentDecision.FSI_DEVICE_NOT_INTERACTIVE, -diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java -index 601771d64046..d2a27b30f36f 100644 ---- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java -+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java -@@ -633,9 +633,30 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { - testShouldFullScreen_notInteractive(); - } - -+ -+ @Test -+ public void testShouldNotFullScreen_isSuppressedByBubbleMetadata() throws RemoteException { -+ NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false); -+ Notification.BubbleMetadata bubbleMetadata = new Notification.BubbleMetadata.Builder("foo") -+ .setSuppressNotification(true).build(); -+ entry.getSbn().getNotification().setBubbleMetadata(bubbleMetadata); -+ when(mPowerManager.isInteractive()).thenReturn(false); -+ when(mDreamManager.isDreaming()).thenReturn(true); -+ when(mStatusBarStateController.getState()).thenReturn(KEYGUARD); -+ -+ assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) -+ .isFalse(); -+ verify(mLogger, never()).logNoFullscreen(any(), any()); -+ verify(mLogger).logNoFullscreenWarning(entry, "GroupAlertBehavior will prevent HUN"); -+ verify(mLogger, never()).logFullscreen(any(), any()); -+ } -+ - @Test - public void testShouldFullScreen_notInteractive() throws RemoteException { - NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false); -+ Notification.BubbleMetadata bubbleMetadata = new Notification.BubbleMetadata.Builder("foo") -+ .setSuppressNotification(false).build(); -+ entry.getSbn().getNotification().setBubbleMetadata(bubbleMetadata); - when(mPowerManager.isInteractive()).thenReturn(false); - when(mDreamManager.isDreaming()).thenReturn(false); - when(mStatusBarStateController.getState()).thenReturn(SHADE); diff --git a/Patches/LineageOS-20.0/android_frameworks_base/ASB-2023-06/0010-Fix-issues-with-setRemotePlaybackInfo.patch b/Patches/LineageOS-20.0/android_frameworks_base/ASB-2023-06/0010-Fix-issues-with-setRemotePlaybackInfo.patch deleted file mode 100644 index f0307671..00000000 --- a/Patches/LineageOS-20.0/android_frameworks_base/ASB-2023-06/0010-Fix-issues-with-setRemotePlaybackInfo.patch +++ /dev/null @@ -1,109 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Beth Thibodeau -Date: Tue, 14 Mar 2023 22:43:54 -0500 -Subject: [PATCH 10/10] Fix issues with setRemotePlaybackInfo - -- Check permissions when building DecoratedMediaCustomViewStyle if it includes -the extras from this API - -- Send device intent as a regular PendingIntent if it can open over lockscreen - -Bug: 271846393 -Test: atest NotificationManagerServiceTest MediaControlPanelTest -Test: manual using test app -(cherry picked from commit 335a3cb7b413fc178f0b190491b870b3327bd7b0) -(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:3c3056c16970d561175192e7a8909a9de784ae54) -Merged-In: Ida43bb4acc34d666e354c16c4344d5c5eb6b333b -Change-Id: Ida43bb4acc34d666e354c16c4344d5c5eb6b333b ---- - .../media/controls/ui/MediaControlPanel.java | 11 ++++-- - .../NotificationManagerService.java | 3 +- - .../NotificationManagerServiceTest.java | 37 +++++++++++++++++++ - 3 files changed, 46 insertions(+), 5 deletions(-) - -diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java -index 5cdfe73ad3aa..e9d8029a1aad 100644 ---- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java -+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java -@@ -620,12 +620,15 @@ public class MediaControlPanel { - } else { - mLogger.logOpenOutputSwitcher(mUid, mPackageName, mInstanceId); - if (device.getIntent() != null) { -- if (device.getIntent().isActivity()) { -- mActivityStarter.startActivity( -- device.getIntent().getIntent(), true); -+ PendingIntent deviceIntent = device.getIntent(); -+ boolean showOverLockscreen = mKeyguardStateController.isShowing() -+ && mActivityIntentHelper.wouldPendingShowOverLockscreen( -+ deviceIntent, mLockscreenUserManager.getCurrentUserId()); -+ if (device.getIntent().isActivity() && !showOverLockscreen) { -+ mActivityStarter.postStartActivityDismissingKeyguard(deviceIntent); - } else { - try { -- device.getIntent().send(); -+ deviceIntent.send(); - } catch (PendingIntent.CanceledException e) { - Log.e(TAG, "Device pending intent was canceled"); - } -diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java -index d16f856376bf..3999c117d40d 100755 ---- a/services/core/java/com/android/server/notification/NotificationManagerService.java -+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java -@@ -6747,7 +6747,8 @@ public class NotificationManagerService extends SystemService { - } - - // Ensure MediaStyle has correct permissions for remote device extras -- if (notification.isStyle(Notification.MediaStyle.class)) { -+ if (notification.isStyle(Notification.MediaStyle.class) -+ || notification.isStyle(Notification.DecoratedMediaCustomViewStyle.class)) { - int hasMediaContentControlPermission = mPackageManager.checkPermission( - android.Manifest.permission.MEDIA_CONTENT_CONTROL, pkg, userId); - if (hasMediaContentControlPermission != PERMISSION_GRANTED) { -diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java -index 3f3b052931ab..9f0a0b2f7628 100755 ---- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java -+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java -@@ -4245,6 +4245,43 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { - - assertFalse(posted.getNotification().extras - .containsKey(Notification.EXTRA_MEDIA_REMOTE_DEVICE)); -+ assertFalse(posted.getNotification().extras -+ .containsKey(Notification.EXTRA_MEDIA_REMOTE_ICON)); -+ assertFalse(posted.getNotification().extras -+ .containsKey(Notification.EXTRA_MEDIA_REMOTE_INTENT)); -+ } -+ -+ @Test -+ public void testCustomMediaStyleRemote_noPermission() throws RemoteException { -+ String deviceName = "device"; -+ when(mPackageManager.checkPermission( -+ eq(android.Manifest.permission.MEDIA_CONTENT_CONTROL), any(), anyInt())) -+ .thenReturn(PERMISSION_DENIED); -+ Notification.DecoratedMediaCustomViewStyle style = -+ new Notification.DecoratedMediaCustomViewStyle(); -+ style.setRemotePlaybackInfo(deviceName, 0, null); -+ Notification.Builder nb = new Notification.Builder(mContext, -+ mTestNotificationChannel.getId()) -+ .setStyle(style); -+ -+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, -+ "testCustomMediaStyleRemoteNoPermission", mUid, 0, -+ nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0); -+ NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel); -+ -+ mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(), -+ nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId()); -+ waitForIdle(); -+ -+ NotificationRecord posted = mService.findNotificationLocked( -+ PKG, nr.getSbn().getTag(), nr.getSbn().getId(), nr.getSbn().getUserId()); -+ -+ assertFalse(posted.getNotification().extras -+ .containsKey(Notification.EXTRA_MEDIA_REMOTE_DEVICE)); -+ assertFalse(posted.getNotification().extras -+ .containsKey(Notification.EXTRA_MEDIA_REMOTE_ICON)); -+ assertFalse(posted.getNotification().extras -+ .containsKey(Notification.EXTRA_MEDIA_REMOTE_INTENT)); - } - - @Test diff --git a/Patches/LineageOS-20.0/android_packages_apps_Settings/ASB-2023-06/0001-Convert-argument-to-intent-in-AddAccountSettings.patch b/Patches/LineageOS-20.0/android_packages_apps_Settings/ASB-2023-06/0001-Convert-argument-to-intent-in-AddAccountSettings.patch deleted file mode 100644 index 5d8e46ce..00000000 --- a/Patches/LineageOS-20.0/android_packages_apps_Settings/ASB-2023-06/0001-Convert-argument-to-intent-in-AddAccountSettings.patch +++ /dev/null @@ -1,28 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Dmitry Dementyev -Date: Tue, 7 Mar 2023 10:36:41 -0800 -Subject: [PATCH 1/2] Convert argument to intent in AddAccountSettings. - -Bug: 265798353 -Test: manual -(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:c7e8052b527434ed8660e3babdab718f7f3cd7da) -Merged-In: I0051e5d5fc9fd3691504cb5fbb959f701e0bce6a -Change-Id: I0051e5d5fc9fd3691504cb5fbb959f701e0bce6a ---- - src/com/android/settings/accounts/AddAccountSettings.java | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/src/com/android/settings/accounts/AddAccountSettings.java b/src/com/android/settings/accounts/AddAccountSettings.java -index 81db4df329..85e942b199 100644 ---- a/src/com/android/settings/accounts/AddAccountSettings.java -+++ b/src/com/android/settings/accounts/AddAccountSettings.java -@@ -103,7 +103,8 @@ public class AddAccountSettings extends Activity { - intent.putExtras(addAccountOptions) - .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); -- startActivityForResultAsUser(intent, ADD_ACCOUNT_REQUEST, mUserHandle); -+ startActivityForResultAsUser( -+ new Intent(intent), ADD_ACCOUNT_REQUEST, mUserHandle); - } else { - setResult(RESULT_OK); - if (mPendingIntent != null) { diff --git a/Patches/LineageOS-20.0/android_packages_apps_Settings/ASB-2023-06/0002-Don-t-show-NLSes-with-excessively-long-component-nam.patch b/Patches/LineageOS-20.0/android_packages_apps_Settings/ASB-2023-06/0002-Don-t-show-NLSes-with-excessively-long-component-nam.patch deleted file mode 100644 index ff4614ac..00000000 --- a/Patches/LineageOS-20.0/android_packages_apps_Settings/ASB-2023-06/0002-Don-t-show-NLSes-with-excessively-long-component-nam.patch +++ /dev/null @@ -1,40 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Julia Reynolds -Date: Tue, 7 Mar 2023 15:44:29 -0500 -Subject: [PATCH 2/2] Don't show NLSes with excessively long component names - -Test: install test app with long CN -Test: ServiceListingTest -Bug: 260570119 -(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:793257967f165970f8cb0f4cebddab9dcd5d8353) -Merged-In: I3ffd02f6cf6bf282e7fc264fd070ed3add4d8571 -Change-Id: I3ffd02f6cf6bf282e7fc264fd070ed3add4d8571 ---- - .../settings/notification/NotificationAccessSettings.java | 7 +++++++ - 1 file changed, 7 insertions(+) - -diff --git a/src/com/android/settings/notification/NotificationAccessSettings.java b/src/com/android/settings/notification/NotificationAccessSettings.java -index 4ec9ccd814..56d3f0e445 100644 ---- a/src/com/android/settings/notification/NotificationAccessSettings.java -+++ b/src/com/android/settings/notification/NotificationAccessSettings.java -@@ -65,6 +65,7 @@ public class NotificationAccessSettings extends EmptyTextSettings { - private static final String TAG = "NotifAccessSettings"; - private static final String ALLOWED_KEY = "allowed"; - private static final String NOT_ALLOWED_KEY = "not_allowed"; -+ private static final int MAX_CN_LENGTH = 500; - - private static final ManagedServiceSettings.Config CONFIG = - new ManagedServiceSettings.Config.Builder() -@@ -101,6 +102,12 @@ public class NotificationAccessSettings extends EmptyTextSettings { - .setNoun(CONFIG.noun) - .setSetting(CONFIG.setting) - .setTag(CONFIG.tag) -+ .setValidator(info -> { -+ if (info.getComponentName().flattenToString().length() > MAX_CN_LENGTH) { -+ return false; -+ } -+ return true; -+ }) - .build(); - mServiceListing.addCallback(this::updateList); - diff --git a/Patches/LineageOS-20.0/android_packages_apps_Traceur/ASB-2023-06/0001-Update-Traceur-to-check-admin-user-status.patch b/Patches/LineageOS-20.0/android_packages_apps_Traceur/ASB-2023-06/0001-Update-Traceur-to-check-admin-user-status.patch deleted file mode 100644 index 0f1db518..00000000 --- a/Patches/LineageOS-20.0/android_packages_apps_Traceur/ASB-2023-06/0001-Update-Traceur-to-check-admin-user-status.patch +++ /dev/null @@ -1,290 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Kevin Jeon -Date: Fri, 17 Feb 2023 20:17:54 +0000 -Subject: [PATCH 1/2] Update Traceur to check admin user status - -This change updates Traceur to check for admin user privileges wherever -a developer options check occurs. This is intended to address the case -in which developer options (a global setting not differentiated on -current user privileges) being enabled would allow guest users to open -Traceur through a 3P app and view its trace files. This would previously -be possible even when ADB debugging was disabled by the admin user. - -Traceur now listens for user changes so that its document root -(containing traces) is enabled/disabled based on the new user's admin -status. - -Test: Using ABTD, apply this on tm-dev+tm-qpr-dev, then check that: - - There are no merge conflicts - - CtsIntentSignatureTestCases passes (b/270791503) - - TraceurUiTests passes - Build+flash a local device on tm-dev+tm-qpr-dev and check that: - - Traceur cannot be opened through 'am start' on a guest account - - Opening Files on a guest account no longer shows a System Traces - folder (even if Traceur's onCreate is somehow called) - - System tracing no longer appears in settings for guests -Bug: 262243665 -Bug: 262244249 -Ignore-AOSP-First: Internal-first security fix -(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:35b3591dc4e50d51d0d7b54eef6cc0c5c6260898) -Merged-In: I1c0c8c9588554378ae39a1a69a35ff44052b93e0 -Change-Id: I1c0c8c9588554378ae39a1a69a35ff44052b93e0 ---- - AndroidManifest.xml | 4 +++ - src/com/android/traceur/MainActivity.java | 5 +++- - src/com/android/traceur/MainTvActivity.java | 5 +++- - src/com/android/traceur/Receiver.java | 29 ++++++++++++++----- - src/com/android/traceur/SearchProvider.java | 7 +++-- - src/com/android/traceur/StopTraceService.java | 7 ++++- - src/com/android/traceur/StorageProvider.java | 8 +++-- - src/com/android/traceur/TraceService.java | 7 ++++- - 8 files changed, 55 insertions(+), 17 deletions(-) - -diff --git a/AndroidManifest.xml b/AndroidManifest.xml -index 6a2544e..f38a71d 100644 ---- a/AndroidManifest.xml -+++ b/AndroidManifest.xml -@@ -47,6 +47,9 @@ - - - -+ -+ -+ - - -@@ -113,6 +116,7 @@ - android:exported="true"> - - -+ - - - -diff --git a/src/com/android/traceur/MainActivity.java b/src/com/android/traceur/MainActivity.java -index 0380dcd..3342652 100644 ---- a/src/com/android/traceur/MainActivity.java -+++ b/src/com/android/traceur/MainActivity.java -@@ -17,6 +17,7 @@ package com.android.traceur; - - import android.app.Activity; - import android.os.Bundle; -+import android.os.UserManager; - import android.provider.Settings; - - import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity; -@@ -34,8 +35,10 @@ public class MainActivity extends CollapsingToolbarBaseActivity { - boolean developerOptionsIsEnabled = - Settings.Global.getInt(getApplicationContext().getContentResolver(), - Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0; -+ boolean isAdminUser = getApplicationContext() -+ .getSystemService(UserManager.class).isAdminUser(); - -- if (!developerOptionsIsEnabled) { -+ if (!developerOptionsIsEnabled || !isAdminUser) { - finish(); - } - } -diff --git a/src/com/android/traceur/MainTvActivity.java b/src/com/android/traceur/MainTvActivity.java -index 91f67c4..7459b7a 100644 ---- a/src/com/android/traceur/MainTvActivity.java -+++ b/src/com/android/traceur/MainTvActivity.java -@@ -17,6 +17,7 @@ package com.android.traceur; - - import android.app.Activity; - import android.os.Bundle; -+import android.os.UserManager; - import android.provider.Settings; - - public class MainTvActivity extends Activity { -@@ -32,8 +33,10 @@ public class MainTvActivity extends Activity { - boolean developerOptionsIsEnabled = - Settings.Global.getInt(getApplicationContext().getContentResolver(), - Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0; -+ boolean isAdminUser = getApplicationContext() -+ .getSystemService(UserManager.class).isAdminUser(); - -- if (!developerOptionsIsEnabled) { -+ if (!developerOptionsIsEnabled || !isAdminUser) { - finish(); - } - } -diff --git a/src/com/android/traceur/Receiver.java b/src/com/android/traceur/Receiver.java -index 0ed5d5d..acadb9a 100644 ---- a/src/com/android/traceur/Receiver.java -+++ b/src/com/android/traceur/Receiver.java -@@ -32,6 +32,7 @@ import android.os.Build; - import android.os.Handler; - import android.os.RemoteException; - import android.os.ServiceManager; -+import android.os.UserManager; - import android.preference.PreferenceManager; - import android.provider.Settings; - import android.text.TextUtils; -@@ -85,6 +86,12 @@ public class Receiver extends BroadcastReceiver { - // We know that Perfetto won't be tracing already at boot, so pass the - // tracingIsOff argument to avoid the Perfetto check. - updateTracing(context, /* assumeTracingIsOff= */ true); -+ } else if (Intent.ACTION_USER_FOREGROUND.equals(intent.getAction())) { -+ boolean developerOptionsEnabled = (1 == -+ Settings.Global.getInt(context.getContentResolver(), -+ Settings.Global.DEVELOPMENT_SETTINGS_ENABLED , 0)); -+ boolean isAdminUser = context.getSystemService(UserManager.class).isAdminUser(); -+ updateStorageProvider(context, developerOptionsEnabled && isAdminUser); - } else if (STOP_ACTION.equals(intent.getAction())) { - prefs.edit().putBoolean( - context.getString(R.string.pref_key_tracing_on), false).commit(); -@@ -213,14 +220,9 @@ public class Receiver extends BroadcastReceiver { - boolean developerOptionsEnabled = (1 == - Settings.Global.getInt(context.getContentResolver(), - Settings.Global.DEVELOPMENT_SETTINGS_ENABLED , 0)); -- -- ComponentName name = new ComponentName(context, -- StorageProvider.class); -- context.getPackageManager().setComponentEnabledSetting(name, -- developerOptionsEnabled -- ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED -- : PackageManager.COMPONENT_ENABLED_STATE_DISABLED, -- PackageManager.DONT_KILL_APP); -+ boolean isAdminUser = context.getSystemService(UserManager.class) -+ .isAdminUser(); -+ updateStorageProvider(context, developerOptionsEnabled && isAdminUser); - - if (!developerOptionsEnabled) { - SharedPreferences prefs = -@@ -243,6 +245,17 @@ public class Receiver extends BroadcastReceiver { - } - } - -+ // Enables/disables the System Traces storage component. enableProvider should be true iff -+ // developer options are enabled and the current user is an admin user. -+ static void updateStorageProvider(Context context, boolean enableProvider) { -+ ComponentName name = new ComponentName(context, StorageProvider.class); -+ context.getPackageManager().setComponentEnabledSetting(name, -+ enableProvider -+ ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED -+ : PackageManager.COMPONENT_ENABLED_STATE_DISABLED, -+ PackageManager.DONT_KILL_APP); -+ } -+ - private static void postCategoryNotification(Context context, SharedPreferences prefs) { - Intent sendIntent = new Intent(context, MainActivity.class); - -diff --git a/src/com/android/traceur/SearchProvider.java b/src/com/android/traceur/SearchProvider.java -index 9586bdb..0d76e9f 100644 ---- a/src/com/android/traceur/SearchProvider.java -+++ b/src/com/android/traceur/SearchProvider.java -@@ -30,6 +30,7 @@ import android.content.Context; - import android.content.Intent; - import android.database.Cursor; - import android.database.MatrixCursor; -+import android.os.UserManager; - import android.provider.SearchIndexablesProvider; - import android.provider.Settings; - -@@ -68,9 +69,11 @@ public class SearchProvider extends SearchIndexablesProvider { - boolean developerOptionsIsEnabled = - Settings.Global.getInt(getContext().getContentResolver(), - Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0; -+ boolean isAdminUser = getContext().getSystemService(UserManager.class).isAdminUser(); - -- // If developer options is not enabled, System Tracing shouldn't be searchable. -- if (!developerOptionsIsEnabled) { -+ // System Tracing shouldn't be searchable if developer options are not enabled or if the -+ // user is not an admin. -+ if (!developerOptionsIsEnabled || !isAdminUser) { - MatrixCursor cursor = new MatrixCursor(NON_INDEXABLES_KEYS_COLUMNS); - Object[] row = new Object[] {getContext().getString(R.string.system_tracing)}; - cursor.addRow(row); -diff --git a/src/com/android/traceur/StopTraceService.java b/src/com/android/traceur/StopTraceService.java -index 20c5f6e..b0c941b 100644 ---- a/src/com/android/traceur/StopTraceService.java -+++ b/src/com/android/traceur/StopTraceService.java -@@ -20,6 +20,7 @@ package com.android.traceur; - import android.content.Context; - import android.content.Intent; - import android.content.SharedPreferences; -+import android.os.UserManager; - import android.preference.PreferenceManager; - import android.provider.Settings; - import android.util.EventLog; -@@ -40,7 +41,7 @@ public class StopTraceService extends TraceService { - @Override - public void onHandleIntent(Intent intent) { - Context context = getApplicationContext(); -- // Checks that developer options are enabled before continuing. -+ // Checks that developer options are enabled and the user is an admin before continuing. - boolean developerOptionsEnabled = - Settings.Global.getInt(context.getContentResolver(), - Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0; -@@ -49,6 +50,10 @@ public class StopTraceService extends TraceService { - EventLog.writeEvent(0x534e4554, "204992293", -1, ""); - return; - } -+ boolean isAdminUser = context.getSystemService(UserManager.class).isAdminUser(); -+ if (!isAdminUser) { -+ return; -+ } - // Ensures that only intents that pertain to stopping a trace and need to be accessed from - // outside Traceur are passed to TraceService through StopTraceService. - String intentAction = intent.getAction(); -diff --git a/src/com/android/traceur/StorageProvider.java b/src/com/android/traceur/StorageProvider.java -index 3df07d5..a2a6c3a 100644 ---- a/src/com/android/traceur/StorageProvider.java -+++ b/src/com/android/traceur/StorageProvider.java -@@ -22,6 +22,7 @@ import android.os.Bundle; - import android.os.FileUtils; - import android.os.CancellationSignal; - import android.os.ParcelFileDescriptor; -+import android.os.UserManager; - import android.provider.DocumentsContract; - import android.provider.DocumentsContract.Document; - import android.provider.DocumentsContract.Root; -@@ -75,10 +76,11 @@ public class StorageProvider extends FileSystemProvider{ - boolean developerOptionsIsEnabled = - Settings.Global.getInt(getContext().getContentResolver(), - Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0; -+ boolean isAdminUser = getContext().getSystemService(UserManager.class).isAdminUser(); - -- // If developer options is not enabled, return an empty root cursor. -- // This removes the provider from the list entirely. -- if (!developerOptionsIsEnabled) { -+ // If developer options is not enabled or the user is not an admin, return an empty root -+ // cursor. This removes the provider from the list entirely. -+ if (!developerOptionsIsEnabled || !isAdminUser) { - return null; - } - -diff --git a/src/com/android/traceur/TraceService.java b/src/com/android/traceur/TraceService.java -index 96e76a8..0039f6f 100644 ---- a/src/com/android/traceur/TraceService.java -+++ b/src/com/android/traceur/TraceService.java -@@ -26,6 +26,7 @@ import android.content.Context; - import android.content.Intent; - import android.content.SharedPreferences; - import android.content.pm.PackageManager; -+import android.os.UserManager; - import android.preference.PreferenceManager; - import android.provider.Settings; - import android.text.format.DateUtils; -@@ -108,7 +109,7 @@ public class TraceService extends IntentService { - @Override - public void onHandleIntent(Intent intent) { - Context context = getApplicationContext(); -- // Checks that developer options are enabled before continuing. -+ // Checks that developer options are enabled and the user is an admin before continuing. - boolean developerOptionsEnabled = - Settings.Global.getInt(context.getContentResolver(), - Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0; -@@ -117,6 +118,10 @@ public class TraceService extends IntentService { - EventLog.writeEvent(0x534e4554, "204992293", -1, ""); - return; - } -+ boolean isAdminUser = context.getSystemService(UserManager.class).isAdminUser(); -+ if (!isAdminUser) { -+ return; -+ } - - if (intent.getAction().equals(INTENT_ACTION_START_TRACING)) { - startTracingInternal(intent.getStringArrayListExtra(INTENT_EXTRA_TAGS), diff --git a/Patches/LineageOS-20.0/android_packages_apps_Traceur/ASB-2023-06/0002-Add-DISALLOW_DEBUGGING_FEATURES-check.patch b/Patches/LineageOS-20.0/android_packages_apps_Traceur/ASB-2023-06/0002-Add-DISALLOW_DEBUGGING_FEATURES-check.patch deleted file mode 100644 index 1ee476f0..00000000 --- a/Patches/LineageOS-20.0/android_packages_apps_Traceur/ASB-2023-06/0002-Add-DISALLOW_DEBUGGING_FEATURES-check.patch +++ /dev/null @@ -1,183 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Kevin Jeon -Date: Wed, 29 Mar 2023 16:38:23 -0400 -Subject: [PATCH 2/2] Add DISALLOW_DEBUGGING_FEATURES check - -This change adds a check for the DISALLOW_DEBUGGING_FEATURES restriction -wherever a developer options or admin-privileges check exists. - -Test: Apply this change to the relevant branches and verify that Traceur - cannot be opened through the researcher-provided APK. -Bug: 270050064 -Bug: 270050191 -Ignore-AOSP-First: Internal-first security fix. -(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:44480ce656dfa33a63bda978b4067bb4e67ee312) -Merged-In: I95d308f6e73a19e489f5eb09558275ca6fb3c4aa -Change-Id: I95d308f6e73a19e489f5eb09558275ca6fb3c4aa ---- - src/com/android/traceur/MainActivity.java | 10 +++++++--- - src/com/android/traceur/MainTvActivity.java | 10 +++++++--- - src/com/android/traceur/Receiver.java | 17 ++++++++++++----- - src/com/android/traceur/SearchProvider.java | 7 +++++-- - src/com/android/traceur/StopTraceService.java | 7 +++++-- - src/com/android/traceur/StorageProvider.java | 7 +++++-- - src/com/android/traceur/TraceService.java | 7 +++++-- - 7 files changed, 46 insertions(+), 19 deletions(-) - -diff --git a/src/com/android/traceur/MainActivity.java b/src/com/android/traceur/MainActivity.java -index 3342652..2d48923 100644 ---- a/src/com/android/traceur/MainActivity.java -+++ b/src/com/android/traceur/MainActivity.java -@@ -35,10 +35,14 @@ public class MainActivity extends CollapsingToolbarBaseActivity { - boolean developerOptionsIsEnabled = - Settings.Global.getInt(getApplicationContext().getContentResolver(), - Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0; -- boolean isAdminUser = getApplicationContext() -- .getSystemService(UserManager.class).isAdminUser(); - -- if (!developerOptionsIsEnabled || !isAdminUser) { -+ UserManager userManager = getApplicationContext() -+ .getSystemService(UserManager.class); -+ boolean isAdminUser = userManager.isAdminUser(); -+ boolean debuggingDisallowed = userManager.hasUserRestriction( -+ UserManager.DISALLOW_DEBUGGING_FEATURES); -+ -+ if (!developerOptionsIsEnabled || !isAdminUser || debuggingDisallowed) { - finish(); - } - } -diff --git a/src/com/android/traceur/MainTvActivity.java b/src/com/android/traceur/MainTvActivity.java -index 7459b7a..de8c2bd 100644 ---- a/src/com/android/traceur/MainTvActivity.java -+++ b/src/com/android/traceur/MainTvActivity.java -@@ -33,10 +33,14 @@ public class MainTvActivity extends Activity { - boolean developerOptionsIsEnabled = - Settings.Global.getInt(getApplicationContext().getContentResolver(), - Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0; -- boolean isAdminUser = getApplicationContext() -- .getSystemService(UserManager.class).isAdminUser(); - -- if (!developerOptionsIsEnabled || !isAdminUser) { -+ UserManager userManager = getApplicationContext() -+ .getSystemService(UserManager.class); -+ boolean isAdminUser = userManager.isAdminUser(); -+ boolean debuggingDisallowed = userManager.hasUserRestriction( -+ UserManager.DISALLOW_DEBUGGING_FEATURES); -+ -+ if (!developerOptionsIsEnabled || !isAdminUser || debuggingDisallowed) { - finish(); - } - } -diff --git a/src/com/android/traceur/Receiver.java b/src/com/android/traceur/Receiver.java -index acadb9a..f3c2ae3 100644 ---- a/src/com/android/traceur/Receiver.java -+++ b/src/com/android/traceur/Receiver.java -@@ -90,8 +90,12 @@ public class Receiver extends BroadcastReceiver { - boolean developerOptionsEnabled = (1 == - Settings.Global.getInt(context.getContentResolver(), - Settings.Global.DEVELOPMENT_SETTINGS_ENABLED , 0)); -- boolean isAdminUser = context.getSystemService(UserManager.class).isAdminUser(); -- updateStorageProvider(context, developerOptionsEnabled && isAdminUser); -+ UserManager userManager = context.getSystemService(UserManager.class); -+ boolean isAdminUser = userManager.isAdminUser(); -+ boolean debuggingDisallowed = userManager.hasUserRestriction( -+ UserManager.DISALLOW_DEBUGGING_FEATURES); -+ updateStorageProvider(context, -+ developerOptionsEnabled && isAdminUser && !debuggingDisallowed); - } else if (STOP_ACTION.equals(intent.getAction())) { - prefs.edit().putBoolean( - context.getString(R.string.pref_key_tracing_on), false).commit(); -@@ -220,9 +224,12 @@ public class Receiver extends BroadcastReceiver { - boolean developerOptionsEnabled = (1 == - Settings.Global.getInt(context.getContentResolver(), - Settings.Global.DEVELOPMENT_SETTINGS_ENABLED , 0)); -- boolean isAdminUser = context.getSystemService(UserManager.class) -- .isAdminUser(); -- updateStorageProvider(context, developerOptionsEnabled && isAdminUser); -+ UserManager userManager = context.getSystemService(UserManager.class); -+ boolean isAdminUser = userManager.isAdminUser(); -+ boolean debuggingDisallowed = userManager.hasUserRestriction( -+ UserManager.DISALLOW_DEBUGGING_FEATURES); -+ updateStorageProvider(context, -+ developerOptionsEnabled && isAdminUser && !debuggingDisallowed); - - if (!developerOptionsEnabled) { - SharedPreferences prefs = -diff --git a/src/com/android/traceur/SearchProvider.java b/src/com/android/traceur/SearchProvider.java -index 0d76e9f..8a20331 100644 ---- a/src/com/android/traceur/SearchProvider.java -+++ b/src/com/android/traceur/SearchProvider.java -@@ -69,11 +69,14 @@ public class SearchProvider extends SearchIndexablesProvider { - boolean developerOptionsIsEnabled = - Settings.Global.getInt(getContext().getContentResolver(), - Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0; -- boolean isAdminUser = getContext().getSystemService(UserManager.class).isAdminUser(); -+ UserManager userManager = getContext().getSystemService(UserManager.class); -+ boolean isAdminUser = userManager.isAdminUser(); -+ boolean debuggingDisallowed = userManager.hasUserRestriction( -+ UserManager.DISALLOW_DEBUGGING_FEATURES); - - // System Tracing shouldn't be searchable if developer options are not enabled or if the - // user is not an admin. -- if (!developerOptionsIsEnabled || !isAdminUser) { -+ if (!developerOptionsIsEnabled || !isAdminUser || debuggingDisallowed) { - MatrixCursor cursor = new MatrixCursor(NON_INDEXABLES_KEYS_COLUMNS); - Object[] row = new Object[] {getContext().getString(R.string.system_tracing)}; - cursor.addRow(row); -diff --git a/src/com/android/traceur/StopTraceService.java b/src/com/android/traceur/StopTraceService.java -index b0c941b..0e754ac 100644 ---- a/src/com/android/traceur/StopTraceService.java -+++ b/src/com/android/traceur/StopTraceService.java -@@ -50,8 +50,11 @@ public class StopTraceService extends TraceService { - EventLog.writeEvent(0x534e4554, "204992293", -1, ""); - return; - } -- boolean isAdminUser = context.getSystemService(UserManager.class).isAdminUser(); -- if (!isAdminUser) { -+ UserManager userManager = context.getSystemService(UserManager.class); -+ boolean isAdminUser = userManager.isAdminUser(); -+ boolean debuggingDisallowed = userManager.hasUserRestriction( -+ UserManager.DISALLOW_DEBUGGING_FEATURES); -+ if (!isAdminUser || debuggingDisallowed) { - return; - } - // Ensures that only intents that pertain to stopping a trace and need to be accessed from -diff --git a/src/com/android/traceur/StorageProvider.java b/src/com/android/traceur/StorageProvider.java -index a2a6c3a..0044a81 100644 ---- a/src/com/android/traceur/StorageProvider.java -+++ b/src/com/android/traceur/StorageProvider.java -@@ -76,11 +76,14 @@ public class StorageProvider extends FileSystemProvider{ - boolean developerOptionsIsEnabled = - Settings.Global.getInt(getContext().getContentResolver(), - Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0; -- boolean isAdminUser = getContext().getSystemService(UserManager.class).isAdminUser(); -+ UserManager userManager = getContext().getSystemService(UserManager.class); -+ boolean isAdminUser = userManager.isAdminUser(); -+ boolean debuggingDisallowed = userManager.hasUserRestriction( -+ UserManager.DISALLOW_DEBUGGING_FEATURES); - - // If developer options is not enabled or the user is not an admin, return an empty root - // cursor. This removes the provider from the list entirely. -- if (!developerOptionsIsEnabled || !isAdminUser) { -+ if (!developerOptionsIsEnabled || !isAdminUser || debuggingDisallowed) { - return null; - } - -diff --git a/src/com/android/traceur/TraceService.java b/src/com/android/traceur/TraceService.java -index 0039f6f..9b6f69d 100644 ---- a/src/com/android/traceur/TraceService.java -+++ b/src/com/android/traceur/TraceService.java -@@ -118,8 +118,11 @@ public class TraceService extends IntentService { - EventLog.writeEvent(0x534e4554, "204992293", -1, ""); - return; - } -- boolean isAdminUser = context.getSystemService(UserManager.class).isAdminUser(); -- if (!isAdminUser) { -+ UserManager userManager = context.getSystemService(UserManager.class); -+ boolean isAdminUser = userManager.isAdminUser(); -+ boolean debuggingDisallowed = userManager.hasUserRestriction( -+ UserManager.DISALLOW_DEBUGGING_FEATURES); -+ if (!isAdminUser || debuggingDisallowed) { - return; - } - diff --git a/Patches/LineageOS-20.0/android_packages_modules_Bluetooth/ASB-2023-06/0001-Fix-OOB-read-in-btm_ble_periodic_av_sync_lost.patch b/Patches/LineageOS-20.0/android_packages_modules_Bluetooth/ASB-2023-06/0001-Fix-OOB-read-in-btm_ble_periodic_av_sync_lost.patch deleted file mode 100644 index 1b69e7e8..00000000 --- a/Patches/LineageOS-20.0/android_packages_modules_Bluetooth/ASB-2023-06/0001-Fix-OOB-read-in-btm_ble_periodic_av_sync_lost.patch +++ /dev/null @@ -1,44 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Brian Delwiche -Date: Wed, 29 Mar 2023 22:52:06 +0000 -Subject: [PATCH 1/2] Fix OOB read in btm_ble_periodic_av_sync_lost - -btm_ble_periodic_av_sync_lost internally calls the function -btm_ble_get_psync_index_from_handle, which polls the internal periodic -sync buffer and returns a matching index if one exists. If no matching -handle is found, it returns MAX_SYNC_TRANSACTION. - -However, here the calling function lacks the check for this case present -in similar functions. If no handle is matched, it will attempt to index -the buffer with MAX_SYNC_TRANSACTION, which will overrun it by a single -width and lead to OOB access. - -Add handling for this case. - -Bug: 273502002 -Test: atest bluetooth_test_gd_unit, atest net_test_stack_btm, validated -against researcher POC -Tag: #security -Ignore-AOSP-First: Security -(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:c077ffbe609c33adc212b73cd3018b174f0c8f89) -Merged-In: I2e1e95b277f81b2668f721a7693df50841968ec5 -Change-Id: I2e1e95b277f81b2668f721a7693df50841968ec5 ---- - system/stack/btm/btm_ble_gap.cc | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/system/stack/btm/btm_ble_gap.cc b/system/stack/btm/btm_ble_gap.cc -index f8a8940d98..af7da7f70c 100644 ---- a/system/stack/btm/btm_ble_gap.cc -+++ b/system/stack/btm/btm_ble_gap.cc -@@ -1180,6 +1180,10 @@ void btm_ble_periodic_adv_sync_lost(uint16_t sync_handle) { - LOG_DEBUG("[PSync]: sync_handle = %d", sync_handle); - - int index = btm_ble_get_psync_index_from_handle(sync_handle); -+ if (index == MAX_SYNC_TRANSACTION) { -+ LOG_ERROR("[PSync]: index not found for handle %u", sync_handle); -+ return; -+ } - tBTM_BLE_PERIODIC_SYNC* ps = &btm_ble_pa_sync_cb.p_sync[index]; - ps->sync_lost_cb.Run(sync_handle); - diff --git a/Patches/LineageOS-20.0/android_packages_modules_Bluetooth/ASB-2023-06/0002-Revert-Revert-Validate-buffer-length-in-sdpu_build_u.patch b/Patches/LineageOS-20.0/android_packages_modules_Bluetooth/ASB-2023-06/0002-Revert-Revert-Validate-buffer-length-in-sdpu_build_u.patch deleted file mode 100644 index d73f2b32..00000000 --- a/Patches/LineageOS-20.0/android_packages_modules_Bluetooth/ASB-2023-06/0002-Revert-Revert-Validate-buffer-length-in-sdpu_build_u.patch +++ /dev/null @@ -1,128 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Brian Delwiche -Date: Tue, 21 Mar 2023 22:34:41 +0000 -Subject: [PATCH 2/2] Revert "Revert "Validate buffer length in - sdpu_build_uuid_seq"" - -This reverts commit e6cf2700522cf639d8115b025833edc24702c7e9. - -Reason for revert: Reinstate original change for QPR -(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:4a33fbcfdb10a16760ef208f1f12a71c1be2d084) -Merged-In: I3e039f1b8f8ffbcc4875b663d417462451fb76a0 -Change-Id: I3e039f1b8f8ffbcc4875b663d417462451fb76a0 ---- - system/stack/sdp/sdp_discovery.cc | 58 ++++++++++++++++++++++++++++--- - 1 file changed, 53 insertions(+), 5 deletions(-) - -diff --git a/system/stack/sdp/sdp_discovery.cc b/system/stack/sdp/sdp_discovery.cc -index 22d6e7caf4..654f899a89 100644 ---- a/system/stack/sdp/sdp_discovery.cc -+++ b/system/stack/sdp/sdp_discovery.cc -@@ -70,10 +70,15 @@ static uint8_t* add_attr(uint8_t* p, uint8_t* p_end, tSDP_DISCOVERY_DB* p_db, - * - ******************************************************************************/ - static uint8_t* sdpu_build_uuid_seq(uint8_t* p_out, uint16_t num_uuids, -- Uuid* p_uuid_list) { -+ Uuid* p_uuid_list, uint16_t& bytes_left) { - uint16_t xx; - uint8_t* p_len; - -+ if (bytes_left < 2) { -+ DCHECK(0) << "SDP: No space for data element header"; -+ return (p_out); -+ } -+ - /* First thing is the data element header */ - UINT8_TO_BE_STREAM(p_out, (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE); - -@@ -81,9 +86,20 @@ static uint8_t* sdpu_build_uuid_seq(uint8_t* p_out, uint16_t num_uuids, - p_len = p_out; - p_out += 1; - -+ /* Account for data element header and length */ -+ bytes_left -= 2; -+ - /* Now, loop through and put in all the UUID(s) */ - for (xx = 0; xx < num_uuids; xx++, p_uuid_list++) { - int len = p_uuid_list->GetShortestRepresentationSize(); -+ -+ if (len + 1 > bytes_left) { -+ DCHECK(0) << "SDP: Too many UUIDs for internal buffer"; -+ break; -+ } else { -+ bytes_left -= (len + 1); -+ } -+ - if (len == Uuid::kNumBytes16) { - UINT8_TO_BE_STREAM(p_out, (UUID_DESC_TYPE << 3) | SIZE_TWO_BYTES); - UINT16_TO_BE_STREAM(p_out, p_uuid_list->As16Bit()); -@@ -120,6 +136,7 @@ static void sdp_snd_service_search_req(tCONN_CB* p_ccb, uint8_t cont_len, - uint8_t *p, *p_start, *p_param_len; - BT_HDR* p_cmd = (BT_HDR*)osi_malloc(SDP_DATA_BUF_SIZE); - uint16_t param_len; -+ uint16_t bytes_left = SDP_DATA_BUF_SIZE; - - /* Prepare the buffer for sending the packet to L2CAP */ - p_cmd->offset = L2CAP_MIN_OFFSET; -@@ -134,9 +151,24 @@ static void sdp_snd_service_search_req(tCONN_CB* p_ccb, uint8_t cont_len, - p_param_len = p; - p += 2; - --/* Build the UID sequence. */ -+ /* Account for header size, max service record count and -+ * continuation state */ -+ const uint16_t base_bytes = (sizeof(BT_HDR) + L2CAP_MIN_OFFSET + -+ 3u + /* service search request header */ -+ 2u + /* param len */ -+ 3u + ((p_cont) ? cont_len : 0)); -+ -+ if (base_bytes > bytes_left) { -+ DCHECK(0) << "SDP: Overran SDP data buffer"; -+ osi_free(p_cmd); -+ return; -+ } -+ -+ bytes_left -= base_bytes; -+ -+ /* Build the UID sequence. */ - p = sdpu_build_uuid_seq(p, p_ccb->p_db->num_uuid_filters, -- p_ccb->p_db->uuid_filters); -+ p_ccb->p_db->uuid_filters, bytes_left); - - /* Set max service record count */ - UINT16_TO_BE_STREAM(p, sdp_cb.max_recs_per_search); -@@ -562,6 +594,7 @@ static void process_service_search_attr_rsp(tCONN_CB* p_ccb, uint8_t* p_reply, - if ((cont_request_needed) || (!p_reply)) { - BT_HDR* p_msg = (BT_HDR*)osi_malloc(SDP_DATA_BUF_SIZE); - uint8_t* p; -+ uint16_t bytes_left = SDP_DATA_BUF_SIZE; - - p_msg->offset = L2CAP_MIN_OFFSET; - p = p_start = (uint8_t*)(p_msg + 1) + L2CAP_MIN_OFFSET; -@@ -575,9 +608,24 @@ static void process_service_search_attr_rsp(tCONN_CB* p_ccb, uint8_t* p_reply, - p_param_len = p; - p += 2; - --/* Build the UID sequence. */ -+ /* Account for header size, max service record count and -+ * continuation state */ -+ const uint16_t base_bytes = (sizeof(BT_HDR) + L2CAP_MIN_OFFSET + -+ 3u + /* service search request header */ -+ 2u + /* param len */ -+ 3u + /* max service record count */ -+ ((p_reply) ? (*p_reply) : 0)); -+ -+ if (base_bytes > bytes_left) { -+ sdp_disconnect(p_ccb, SDP_INVALID_CONT_STATE); -+ return; -+ } -+ -+ bytes_left -= base_bytes; -+ -+ /* Build the UID sequence. */ - p = sdpu_build_uuid_seq(p, p_ccb->p_db->num_uuid_filters, -- p_ccb->p_db->uuid_filters); -+ p_ccb->p_db->uuid_filters, bytes_left); - - /* Max attribute byte count */ - UINT16_TO_BE_STREAM(p, sdp_cb.max_attr_list_size); diff --git a/Patches/LineageOS-20.0/android_packages_modules_Wifi/ASB-2023-06/0001-Don-t-send-credentials-in-an-unauthenticated-TLS-tun.patch b/Patches/LineageOS-20.0/android_packages_modules_Wifi/ASB-2023-06/0001-Don-t-send-credentials-in-an-unauthenticated-TLS-tun.patch deleted file mode 100644 index a3a5ba86..00000000 --- a/Patches/LineageOS-20.0/android_packages_modules_Wifi/ASB-2023-06/0001-Don-t-send-credentials-in-an-unauthenticated-TLS-tun.patch +++ /dev/null @@ -1,1692 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Jimmy Chen -Date: Wed, 28 Dec 2022 15:19:08 +0800 -Subject: [PATCH 1/3] Don't send credentials in an unauthenticated TLS tunnel - -Fix the security vulnerability reported in the security report. -Do not send the user credentials in phase2 before the user -approves the server certificate. This is done by connecting -with no credentials for the purpose of getting the server -certificate chain only, and reconnecting once the user approves -with full certificate chain authentication. - -Updated-PDD: TRUE -Bug: 250574778 -Bug: 251910611 -Test: atest InsecureEapNetworkHandlerTest ClientModeImplTest -Test: Integration test with WPA-Enterprise network with an S -device and a T device. -(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:fb630e6a13189321d0e83037adc9e7b30dc6d796) -Merged-In: Ib3501f5e04881b11ea9ab52472dd233f2aa82c7a -Change-Id: Ib3501f5e04881b11ea9ab52472dd233f2aa82c7a ---- - .../res/values/overlayable.xml | 24 ++ - .../res/values/strings.xml | 3 + - .../android/server/wifi/ClientModeImpl.java | 56 ++- - .../wifi/InsecureEapNetworkHandler.java | 394 +++++++++++++----- - .../android/server/wifi/WifiServiceImpl.java | 7 +- - service/proto/src/metrics.proto | 3 + - .../server/wifi/ClientModeImplTest.java | 136 ++++-- - .../wifi/InsecureEapNetworkHandlerTest.java | 342 +++++++++++++-- - 8 files changed, 745 insertions(+), 220 deletions(-) - -diff --git a/service/ServiceWifiResources/res/values/overlayable.xml b/service/ServiceWifiResources/res/values/overlayable.xml -index 2eef3f5156..75c90a3887 100644 ---- a/service/ServiceWifiResources/res/values/overlayable.xml -+++ b/service/ServiceWifiResources/res/values/overlayable.xml -@@ -321,6 +321,30 @@ - - - -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ - - - -diff --git a/service/ServiceWifiResources/res/values/strings.xml b/service/ServiceWifiResources/res/values/strings.xml -index 15ffcf0ada..db331b2e7b 100644 ---- a/service/ServiceWifiResources/res/values/strings.xml -+++ b/service/ServiceWifiResources/res/values/strings.xml -@@ -205,6 +205,9 @@ - Network needs to be verified - Review network details for %1$s before connecting. Tap to continue. - Certificate installation failed. -+ Can\'t connect to %1$s -+ The server certificate chain is invalid. -+ OK - - - Can\'t verify this network -diff --git a/service/java/com/android/server/wifi/ClientModeImpl.java b/service/java/com/android/server/wifi/ClientModeImpl.java -index 931374bccb..9ec12bdad8 100644 ---- a/service/java/com/android/server/wifi/ClientModeImpl.java -+++ b/service/java/com/android/server/wifi/ClientModeImpl.java -@@ -580,9 +580,6 @@ public class ClientModeImpl extends StateMachine implements ClientMode { - @VisibleForTesting - static final int CMD_ACCEPT_EAP_SERVER_CERTIFICATE = BASE + 301; - -- @VisibleForTesting -- static final int CMD_REJECT_EAP_SERVER_CERTIFICATE = BASE + 302; -- - /* Tracks if suspend optimizations need to be disabled by DHCP, - * screen or due to high perf mode. - * When any of them needs to disable it, we keep the suspend optimizations -@@ -837,17 +834,11 @@ public class ClientModeImpl extends StateMachine implements ClientMode { - @Override - public void onReject(String ssid) { - log("Reject Root CA cert for " + ssid); -- sendMessage(CMD_REJECT_EAP_SERVER_CERTIFICATE, -- WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_REJECTED_BY_USER, -- 0, ssid); - } - - @Override - public void onError(String ssid) { - log("Insecure EAP network error for " + ssid); -- sendMessage(CMD_REJECT_EAP_SERVER_CERTIFICATE, -- WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_EAP_FAILURE, -- 0, ssid); - }}; - mInsecureEapNetworkHandler = new InsecureEapNetworkHandler( - mContext, -@@ -2261,8 +2252,6 @@ public class ClientModeImpl extends StateMachine implements ClientMode { - return "CMD_UPDATE_LINKPROPERTIES"; - case CMD_ACCEPT_EAP_SERVER_CERTIFICATE: - return "CMD_ACCEPT_EAP_SERVER_CERTIFICATE"; -- case CMD_REJECT_EAP_SERVER_CERTIFICATE: -- return "CMD_REJECT_EAP_SERVER_CERTIFICATE"; - case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT: - return "SUPPLICANT_STATE_CHANGE_EVENT"; - case WifiMonitor.AUTHENTICATION_FAILURE_EVENT: -@@ -4021,8 +4010,21 @@ public class ClientModeImpl extends StateMachine implements ClientMode { - break; - } - } -- mInsecureEapNetworkHandler.prepareConnection(mTargetWifiConfiguration); - setSelectedRcoiForPasspoint(config); -+ if (mInsecureEapNetworkHandler.prepareConnection(mTargetWifiConfiguration)) { -+ /* If TOFU is not supported and the user did not approve to connect to an -+ insecure network before, do not connect now and instead, display a dialog -+ or a notification, and keep network disconnected to avoid sending the -+ credentials. -+ */ -+ mInsecureEapNetworkHandler.startUserApprovalIfNecessary(mIsUserSelected); -+ reportConnectionAttemptEnd( -+ WifiMetrics.ConnectionEvent.FAILURE_CONNECT_NETWORK_FAILED, -+ WifiMetricsProto.ConnectionEvent.HLF_NONE, -+ WifiMetricsProto.ConnectionEvent.DISCONNECTED_USER_APPROVAL_NEEDED); -+ transitionTo(mDisconnectedState); -+ break; -+ } - connectToNetwork(config); - break; - } -@@ -4277,7 +4279,6 @@ public class ClientModeImpl extends StateMachine implements ClientMode { - break; - } - case CMD_ACCEPT_EAP_SERVER_CERTIFICATE: -- case CMD_REJECT_EAP_SERVER_CERTIFICATE: - case CMD_START_ROAM: - case CMD_START_RSSI_MONITORING_OFFLOAD: - case CMD_STOP_RSSI_MONITORING_OFFLOAD: -@@ -5384,11 +5385,16 @@ public class ClientModeImpl extends StateMachine implements ClientMode { - } - case WifiMonitor.TOFU_ROOT_CA_CERTIFICATE: - if (null == mTargetWifiConfiguration) break; -- if (!mInsecureEapNetworkHandler.setPendingCertificate( -+ int certificateDepth = message.arg2; -+ if (!mInsecureEapNetworkHandler.addPendingCertificate( - mTargetWifiConfiguration.SSID, message.arg2, - (X509Certificate) message.obj)) { - Log.d(TAG, "Cannot set pending cert."); - } -+ // Launch user approval upon receiving the server certificate -+ if (certificateDepth == 0) { -+ mInsecureEapNetworkHandler.startUserApprovalIfNecessary(mIsUserSelected); -+ } - break; - default: { - handleStatus = NOT_HANDLED; -@@ -5867,10 +5873,6 @@ public class ClientModeImpl extends StateMachine implements ClientMode { - class L3ProvisioningState extends State { - @Override - public void enter() { -- if (mInsecureEapNetworkHandler.startUserApprovalIfNecessary(mIsUserSelected)) { -- return; -- } -- - startL3Provisioning(); - } - -@@ -5890,18 +5892,6 @@ public class ClientModeImpl extends StateMachine implements ClientMode { - handleStatus = NOT_HANDLED; - break; - } -- case CMD_ACCEPT_EAP_SERVER_CERTIFICATE: -- startL3Provisioning(); -- break; -- case CMD_REJECT_EAP_SERVER_CERTIFICATE: { -- int l2FailureReason = message.arg1; -- reportConnectionAttemptEnd( -- WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION, -- WifiMetricsProto.ConnectionEvent.HLF_NONE, -- l2FailureReason); -- mWifiNative.disconnect(mInterfaceName); -- break; -- } - default: { - handleStatus = NOT_HANDLED; - break; -@@ -6421,6 +6411,12 @@ public class ClientModeImpl extends StateMachine implements ClientMode { - } - break; - } -+ case CMD_ACCEPT_EAP_SERVER_CERTIFICATE: -+ // Got an approval for a TOFU network, trigger a scan to accelerate the -+ // auto-connection. -+ logd("User accepted TOFU provided certificate"); -+ mWifiConnectivityManager.forceConnectivityScan(ClientModeImpl.WIFI_WORK_SOURCE); -+ break; - default: { - handleStatus = NOT_HANDLED; - break; -diff --git a/service/java/com/android/server/wifi/InsecureEapNetworkHandler.java b/service/java/com/android/server/wifi/InsecureEapNetworkHandler.java -index 225d01c7e5..b7389952e0 100644 ---- a/service/java/com/android/server/wifi/InsecureEapNetworkHandler.java -+++ b/service/java/com/android/server/wifi/InsecureEapNetworkHandler.java -@@ -40,6 +40,8 @@ import com.android.server.wifi.util.NativeUtil; - import com.android.wifi.resources.R; - - import java.security.cert.X509Certificate; -+import java.util.ArrayList; -+import java.util.List; - - /** This class is used to handle insecure EAP networks. */ - public class InsecureEapNetworkHandler { -@@ -71,18 +73,27 @@ public class InsecureEapNetworkHandler { - private final String mInterfaceName; - private final Handler mHandler; - -+ // The latest connecting configuration from the caller, it is updated on calling -+ // prepareConnection() always. This is used to ensure that current TOFU config is aligned -+ // with the caller connecting config. - @NonNull -- private WifiConfiguration mCurConfig = null; -- private int mPendingCaCertDepth = -1; -+ private WifiConfiguration mConnectingConfig = null; -+ // The connecting configuration which is a valid TOFU configuration, it is updated -+ // only when the connecting configuration is a valid TOFU configuration and used -+ // by later TOFU procedure. -+ @NonNull -+ private WifiConfiguration mCurrentTofuConfig = null; -+ private int mPendingRootCaCertDepth = -1; - @Nullable -- private X509Certificate mPendingCaCert = null; -+ private X509Certificate mPendingRootCaCert = null; - @Nullable - private X509Certificate mPendingServerCert = null; -- // This is updated on setting a pending CA cert. -- private CertificateSubjectInfo mPendingCaCertSubjectInfo = null; -- // This is updated on setting a pending CA cert. -- private CertificateSubjectInfo mPendingCaCertIssuerInfo = null; -- @Nullable -+ // This is updated on setting a pending server cert. -+ private CertificateSubjectInfo mPendingServerCertSubjectInfo = null; -+ // This is updated on setting a pending server cert. -+ private CertificateSubjectInfo mPendingServerCertIssuerInfo = null; -+ // Record the whole server cert chain from Root CA to the server cert. -+ private List mServerCertChain = new ArrayList<>(); - private WifiDialogManager.DialogHandle mTofuAlertDialog = null; - private boolean mIsCertNotificationReceiverRegistered = false; - -@@ -137,16 +148,17 @@ public class InsecureEapNetworkHandler { - * uses Server Cert, without a valid Root CA certificate or user approval. - * - * @param config the running wifi configuration. -+ * @return true if user needs to be notified about an insecure network but TOFU is not supported -+ * by the device, or false otherwise. - */ -- public void prepareConnection(@NonNull WifiConfiguration config) { -- if (null == config) return; -+ public boolean prepareConnection(@NonNull WifiConfiguration config) { -+ if (null == config) return false; -+ mConnectingConfig = config; - -- if (!config.isEnterprise()) return; -+ if (!config.isEnterprise()) return false; - WifiEnterpriseConfig entConfig = config.enterpriseConfig; -- if (!entConfig.isEapMethodServerCertUsed()) return; -- if (entConfig.hasCaCertificate()) return; -- -- clearConnection(); -+ if (!entConfig.isEapMethodServerCertUsed()) return false; -+ if (entConfig.hasCaCertificate()) return false; - - Log.d(TAG, "prepareConnection: isTofuSupported=" + mIsTrustOnFirstUseSupported - + ", isInsecureEapNetworkAllowed=" + mIsInsecureEnterpriseConfigurationAllowed -@@ -155,71 +167,115 @@ public class InsecureEapNetworkHandler { - // If TOFU is not supported or insecure EAP network is allowed without TOFU enabled, - // return to skip the dialog if this network is approved before. - if (entConfig.isUserApproveNoCaCert()) { -- if (!mIsTrustOnFirstUseSupported) return; -+ if (!mIsTrustOnFirstUseSupported) return false; - if (mIsInsecureEnterpriseConfigurationAllowed - && !entConfig.isTrustOnFirstUseEnabled()) { -- return; -+ return false; - } - } - -- mCurConfig = config; -+ if (mIsTrustOnFirstUseSupported) { -+ /** -+ * Clear the user credentials from this copy of the configuration object. -+ * Supplicant will start the phase-1 TLS session to acquire the server certificate chain -+ * which will be provided to the framework. Then since the callbacks for identity and -+ * password requests are not populated, it will fail the connection and disconnect. -+ * This will allow the user to review the certificates at their own pace, and a -+ * reconnection would automatically take place with full verification of the chain once -+ * they approve. -+ */ -+ if (config.enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.TTLS -+ || config.enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.PEAP) { -+ config.enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.NONE); -+ config.enterpriseConfig.setIdentity(null); -+ config.enterpriseConfig.setPassword(null); -+ } else if (config.enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.TLS) { -+ config.enterpriseConfig.setClientCertificateAlias(null); -+ } -+ } -+ mCurrentTofuConfig = config; -+ mServerCertChain.clear(); -+ dismissDialogAndNotification(); - registerCertificateNotificationReceiver(); -- // Remove cached PMK in the framework and supplicant to avoid -- // skipping the EAP flow. -- clearNativeData(); -- Log.d(TAG, "Remove native cached data and networks for TOFU."); -+ if (!mIsTrustOnFirstUseSupported) { -+ /** -+ * Devices with no TOFU support, do not connect to the network until the user is -+ * aware that the network is insecure, and approves the connection. -+ */ -+ putNetworkOnHold(false); -+ } else { -+ // Remove cached PMK in the framework and supplicant to avoid skipping the EAP flow. -+ clearNativeData(); -+ Log.d(TAG, "Remove native cached data and networks for TOFU."); -+ } -+ return !mIsTrustOnFirstUseSupported; - } - -- /** Clear data on disconnecting a connection. */ -- private void clearConnection() { -- unregisterCertificateNotificationReceiver(); -+ /** -+ * Do necessary clean up on stopping client mode. -+ */ -+ public void cleanup() { - dismissDialogAndNotification(); -+ unregisterCertificateNotificationReceiver(); - clearInternalData(); - } - - /** -- * Store the received certifiate for later use. -+ * Stores a received certificate for later use. - * - * @param ssid the target network SSID. - * @param depth the depth of this cert. The Root CA should be 0 or - * a positive number, and the server cert is 0. -- * @param cert the Root CA certificate from the server. -+ * @param cert a certificate from the server. - * @return true if the cert is cached; otherwise, false. - */ -- public boolean setPendingCertificate(@NonNull String ssid, int depth, -+ public boolean addPendingCertificate(@NonNull String ssid, int depth, - @NonNull X509Certificate cert) { -+ String configProfileKey = mCurrentTofuConfig != null -+ ? mCurrentTofuConfig.getProfileKey() : "null"; - Log.d(TAG, "setPendingCertificate: " + "ssid=" + ssid + " depth=" + depth -- + " current config=" + mCurConfig); -+ + " current config=" + configProfileKey); - if (TextUtils.isEmpty(ssid)) return false; -- if (null == mCurConfig) return false; -- if (!TextUtils.equals(ssid, mCurConfig.SSID)) return false; -+ if (null == mCurrentTofuConfig) return false; -+ if (!TextUtils.equals(ssid, mCurrentTofuConfig.SSID)) return false; - if (null == cert) return false; - if (depth < 0) return false; -+ -+ if (depth == 0) { -+ // Disable network selection upon receiving the server certificate -+ putNetworkOnHold(true); -+ } -+ -+ if (!mServerCertChain.contains(cert)) { -+ mServerCertChain.add(cert); -+ } -+ - // 0 is the tail, i.e. the server cert. - if (depth == 0 && null == mPendingServerCert) { - mPendingServerCert = cert; - Log.d(TAG, "Pending server certificate: " + mPendingServerCert); -+ mPendingServerCertSubjectInfo = CertificateSubjectInfo.parse( -+ cert.getSubjectX500Principal().getName()); -+ if (null == mPendingServerCertSubjectInfo) { -+ Log.e(TAG, "CA cert has no valid subject."); -+ return false; -+ } -+ mPendingServerCertIssuerInfo = CertificateSubjectInfo.parse( -+ cert.getIssuerX500Principal().getName()); -+ if (null == mPendingServerCertIssuerInfo) { -+ Log.e(TAG, "CA cert has no valid issuer."); -+ return false; -+ } - } -- if (depth < mPendingCaCertDepth) { -+ -+ // Root or intermediate cert. -+ if (depth < mPendingRootCaCertDepth) { - Log.d(TAG, "Ignore intermediate cert." + cert); - return true; - } -- -- mPendingCaCertSubjectInfo = CertificateSubjectInfo.parse( -- cert.getSubjectDN().getName()); -- if (null == mPendingCaCertSubjectInfo) { -- Log.e(TAG, "CA cert has no valid subject."); -- return false; -- } -- mPendingCaCertIssuerInfo = CertificateSubjectInfo.parse( -- cert.getIssuerDN().getName()); -- if (null == mPendingCaCertIssuerInfo) { -- Log.e(TAG, "CA cert has no valid issuer."); -- return false; -- } -- mPendingCaCertDepth = depth; -- mPendingCaCert = cert; -- Log.d(TAG, "Pending Root CA certificate: " + mPendingCaCert); -+ mPendingRootCaCertDepth = depth; -+ mPendingRootCaCert = cert; -+ Log.d(TAG, "Pending Root CA certificate: " + mPendingRootCaCert); - return true; - } - -@@ -244,31 +300,89 @@ public class InsecureEapNetworkHandler { - * @return true if the user approval is needed; otherwise, false. - */ - public boolean startUserApprovalIfNecessary(boolean isUserSelected) { -- if (null == mCurConfig) return false; -- if (!mCurConfig.isEnterprise()) return false; -- WifiEnterpriseConfig entConfig = mCurConfig.enterpriseConfig; -- if (!entConfig.isEapMethodServerCertUsed()) return false; -- if (entConfig.hasCaCertificate()) return false; -+ if (null == mConnectingConfig || null == mCurrentTofuConfig) return false; -+ if (mConnectingConfig.networkId != mCurrentTofuConfig.networkId) return false; - - // If Trust On First Use is supported and insecure enterprise configuration - // is not allowed, TOFU must be used for an Enterprise network without certs. - if (mIsTrustOnFirstUseSupported && !mIsInsecureEnterpriseConfigurationAllowed -- && !mCurConfig.enterpriseConfig.isTrustOnFirstUseEnabled()) { -+ && !mCurrentTofuConfig.enterpriseConfig.isTrustOnFirstUseEnabled()) { - Log.d(TAG, "Trust On First Use is not enabled."); -- handleError(mCurConfig.SSID); -+ handleError(mCurrentTofuConfig.SSID); - return true; - } - - if (useTrustOnFirstUse()) { -- if (null == mPendingCaCert) { -- Log.d(TAG, "No valid CA cert for TLS-based connection."); -- handleError(mCurConfig.SSID); -+ if (null == mPendingRootCaCert) { -+ Log.e(TAG, "No valid CA cert for TLS-based connection."); -+ handleError(mCurrentTofuConfig.SSID); - return true; - } else if (null == mPendingServerCert) { -- Log.d(TAG, "No valid Server cert for TLS-based connection."); -- handleError(mCurConfig.SSID); -+ Log.e(TAG, "No valid Server cert for TLS-based connection."); -+ handleError(mCurrentTofuConfig.SSID); -+ return true; -+ } else if (!isServerCertChainValid()) { -+ Log.e(TAG, "Server cert chain is invalid."); -+ String title = mContext.getString(R.string.wifi_tofu_invalid_cert_chain_title, -+ mCurrentTofuConfig.SSID); -+ String message = mContext.getString(R.string.wifi_tofu_invalid_cert_chain_message); -+ String okButtonText = mContext.getString( -+ R.string.wifi_tofu_invalid_cert_chain_ok_text); -+ -+ handleError(mCurrentTofuConfig.SSID); -+ -+ if (TextUtils.isEmpty(title) || TextUtils.isEmpty(message)) return true; -+ -+ if (isUserSelected) { -+ mTofuAlertDialog = mWifiDialogManager.createSimpleDialog( -+ title, -+ message, -+ null /* positiveButtonText */, -+ null /* negativeButtonText */, -+ okButtonText, -+ new WifiDialogManager.SimpleDialogCallback() { -+ @Override -+ public void onPositiveButtonClicked() { -+ // Not used. -+ } -+ -+ @Override -+ public void onNegativeButtonClicked() { -+ // Not used. -+ } -+ -+ @Override -+ public void onNeutralButtonClicked() { -+ // Not used. -+ } -+ -+ @Override -+ public void onCancelled() { -+ // Not used. -+ } -+ }, -+ new WifiThreadRunner(mHandler)); -+ mTofuAlertDialog.launchDialog(); -+ } else { -+ Notification.Builder builder = mFacade.makeNotificationBuilder(mContext, -+ WifiService.NOTIFICATION_NETWORK_ALERTS) -+ .setSmallIcon( -+ Icon.createWithResource(mContext.getWifiOverlayApkPkgName(), -+ com.android.wifi.resources.R -+ .drawable.stat_notify_wifi_in_range)) -+ .setContentTitle(title) -+ .setContentText(message) -+ .setStyle(new Notification.BigTextStyle().bigText(message)) -+ .setColor(mContext.getResources().getColor( -+ android.R.color.system_notification_accent_color)); -+ mNotificationManager.notify(SystemMessage.NOTE_SERVER_CA_CERTIFICATE, -+ builder.build()); -+ } - return true; - } -+ } else if (mIsInsecureEnterpriseConfigurationAllowed) { -+ Log.i(TAG, "networks without the server cert are allowed, skip it."); -+ return false; - } - - Log.d(TAG, "startUserApprovalIfNecessaryForInsecureEapNetwork: mIsUserSelected=" -@@ -282,13 +396,56 @@ public class InsecureEapNetworkHandler { - return true; - } - -+ /** -+ * Disable network selection, disconnect if necessary, and clear PMK cache -+ */ -+ private void putNetworkOnHold(boolean needToDisconnect) { -+ // Disable network selection upon receiving the server certificate -+ mWifiConfigManager.updateNetworkSelectionStatus(mCurrentTofuConfig.networkId, -+ WifiConfiguration.NetworkSelectionStatus -+ .DISABLED_BY_WIFI_MANAGER); -+ -+ // Force disconnect and clear PMK cache to avoid supplicant reconnection -+ if (needToDisconnect) mWifiNative.disconnect(mInterfaceName); -+ clearNativeData(); -+ } -+ -+ private boolean isServerCertChainValid() { -+ if (mServerCertChain.size() == 0) return false; -+ -+ X509Certificate parentCert = null; -+ for (X509Certificate cert: mServerCertChain) { -+ String subject = cert.getSubjectX500Principal().getName(); -+ String issuer = cert.getIssuerX500Principal().getName(); -+ boolean isCa = cert.getBasicConstraints() >= 0; -+ Log.d(TAG, "Subject: " + subject + ", Issuer: " + issuer + ", isCA: " + isCa); -+ -+ if (parentCert == null) { -+ // The root cert, it should be a CA cert or a self-signed cert. -+ if (!isCa && !subject.equals(issuer)) { -+ Log.e(TAG, "The root cert is not a CA cert or a self-signed cert."); -+ return false; -+ } -+ } else { -+ // The issuer of intermediate cert of the leaf cert should be -+ // the same as the subject of its parent cert. -+ if (!parentCert.getSubjectX500Principal().getName().equals(issuer)) { -+ Log.e(TAG, "The issuer does not match the subject of its parent."); -+ return false; -+ } -+ } -+ parentCert = cert; -+ } -+ return true; -+ } -+ - private boolean useTrustOnFirstUse() { - return mIsTrustOnFirstUseSupported -- && mCurConfig.enterpriseConfig.isTrustOnFirstUseEnabled(); -+ && mCurrentTofuConfig.enterpriseConfig.isTrustOnFirstUseEnabled(); - } - - private void registerCertificateNotificationReceiver() { -- if (mIsCertNotificationReceiverRegistered) return; -+ unregisterCertificateNotificationReceiver(); - - IntentFilter filter = new IntentFilter(); - if (useTrustOnFirstUse()) { -@@ -313,21 +470,22 @@ public class InsecureEapNetworkHandler { - if (!isConnectionValid(ssid)) return; - - if (!useTrustOnFirstUse()) { -- mWifiConfigManager.setUserApproveNoCaCert(mCurConfig.networkId, true); -+ mWifiConfigManager.setUserApproveNoCaCert(mCurrentTofuConfig.networkId, true); - } else { -- if (null == mPendingCaCert || null == mPendingServerCert) { -+ if (null == mPendingRootCaCert || null == mPendingServerCert) { - handleError(ssid); - return; - } - if (!mWifiConfigManager.updateCaCertificate( -- mCurConfig.networkId, mPendingCaCert, mPendingServerCert)) { -+ mCurrentTofuConfig.networkId, mPendingRootCaCert, mPendingServerCert)) { - // The user approved this network, - // keep the connection regardless of the result. -- Log.e(TAG, "Cannot update CA cert to network " + mCurConfig.getProfileKey() -- + ", CA cert = " + mPendingCaCert); -+ Log.e(TAG, "Cannot update CA cert to network " + mCurrentTofuConfig.getProfileKey() -+ + ", CA cert = " + mPendingRootCaCert); - } - } -- mWifiConfigManager.allowAutojoin(mCurConfig.networkId, true); -+ mWifiConfigManager.updateNetworkSelectionStatus(mCurrentTofuConfig.networkId, -+ WifiConfiguration.NetworkSelectionStatus.DISABLED_NONE); - dismissDialogAndNotification(); - clearInternalData(); - -@@ -338,7 +496,8 @@ public class InsecureEapNetworkHandler { - void handleReject(@NonNull String ssid) { - if (!isConnectionValid(ssid)) return; - -- mWifiConfigManager.allowAutojoin(mCurConfig.networkId, false); -+ mWifiConfigManager.updateNetworkSelectionStatus(mCurrentTofuConfig.networkId, -+ WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER); - dismissDialogAndNotification(); - clearInternalData(); - clearNativeData(); -@@ -347,6 +506,11 @@ public class InsecureEapNetworkHandler { - } - - private void handleError(@Nullable String ssid) { -+ if (mCurrentTofuConfig != null) { -+ mWifiConfigManager.updateNetworkSelectionStatus(mCurrentTofuConfig.networkId, -+ WifiConfiguration.NetworkSelectionStatus -+ .DISABLED_BY_WIFI_MANAGER); -+ } - dismissDialogAndNotification(); - clearInternalData(); - clearNativeData(); -@@ -355,9 +519,9 @@ public class InsecureEapNetworkHandler { - } - - private void askForUserApprovalForCaCertificate() { -- if (mCurConfig == null || TextUtils.isEmpty(mCurConfig.SSID)) return; -+ if (mCurrentTofuConfig == null || TextUtils.isEmpty(mCurrentTofuConfig.SSID)) return; - if (useTrustOnFirstUse()) { -- if (null == mPendingCaCert || null == mPendingServerCert) { -+ if (null == mPendingRootCaCert || null == mPendingServerCert) { - Log.e(TAG, "Cannot launch a dialog for TOFU without " - + "a valid pending CA certificate."); - return; -@@ -375,39 +539,42 @@ public class InsecureEapNetworkHandler { - ? mContext.getString(R.string.wifi_ca_cert_dialog_abort_text) - : mContext.getString(R.string.wifi_ca_cert_dialog_preT_abort_text); - -- String message = null; -+ String message; - String messageUrl = null; - int messageUrlStart = 0; - int messageUrlEnd = 0; - if (useTrustOnFirstUse()) { -- String signature = NativeUtil.hexStringFromByteArray( -- mPendingCaCert.getSignature()); - StringBuilder contentBuilder = new StringBuilder() - .append(mContext.getString(R.string.wifi_ca_cert_dialog_message_hint)) - .append(mContext.getString( - R.string.wifi_ca_cert_dialog_message_server_name_text, -- mPendingCaCertSubjectInfo.commonName)) -+ mPendingServerCertSubjectInfo.commonName)) - .append(mContext.getString( - R.string.wifi_ca_cert_dialog_message_issuer_name_text, -- mPendingCaCertIssuerInfo.commonName)); -- if (!TextUtils.isEmpty(mPendingCaCertSubjectInfo.organization)) { -+ mPendingServerCertIssuerInfo.commonName)); -+ if (!TextUtils.isEmpty(mPendingServerCertSubjectInfo.organization)) { - contentBuilder.append(mContext.getString( - R.string.wifi_ca_cert_dialog_message_organization_text, -- mPendingCaCertSubjectInfo.organization)); -+ mPendingServerCertSubjectInfo.organization)); - } -- if (!TextUtils.isEmpty(mPendingCaCertSubjectInfo.email)) { -+ if (!TextUtils.isEmpty(mPendingServerCertSubjectInfo.email)) { - contentBuilder.append(mContext.getString( - R.string.wifi_ca_cert_dialog_message_contact_text, -- mPendingCaCertSubjectInfo.email)); -+ mPendingServerCertSubjectInfo.email)); -+ } -+ byte[] signature = mPendingServerCert.getSignature(); -+ if (signature != null) { -+ String signatureString = NativeUtil.hexStringFromByteArray(signature); -+ if (signatureString.length() > 16) { -+ signatureString = signatureString.substring(0, 16); -+ } -+ contentBuilder.append(mContext.getString( -+ R.string.wifi_ca_cert_dialog_message_signature_name_text, signatureString)); - } -- contentBuilder -- .append(mContext.getString( -- R.string.wifi_ca_cert_dialog_message_signature_name_text, -- signature.substring(0, 16))); - message = contentBuilder.toString(); - } else { - String hint = mContext.getString( -- R.string.wifi_ca_cert_dialog_preT_message_hint, mCurConfig.SSID); -+ R.string.wifi_ca_cert_dialog_preT_message_hint, mCurrentTofuConfig.SSID); - String linkText = mContext.getString( - R.string.wifi_ca_cert_dialog_preT_message_link); - message = hint + " " + linkText; -@@ -427,23 +594,35 @@ public class InsecureEapNetworkHandler { - new WifiDialogManager.SimpleDialogCallback() { - @Override - public void onPositiveButtonClicked() { -- handleAccept(mCurConfig.SSID); -+ if (mCurrentTofuConfig == null) { -+ return; -+ } -+ handleAccept(mCurrentTofuConfig.SSID); - } - - @Override - public void onNegativeButtonClicked() { -- handleReject(mCurConfig.SSID); -+ if (mCurrentTofuConfig == null) { -+ return; -+ } -+ handleReject(mCurrentTofuConfig.SSID); - } - - @Override - public void onNeutralButtonClicked() { - // Not used. -- handleReject(mCurConfig.SSID); -+ if (mCurrentTofuConfig == null) { -+ return; -+ } -+ handleReject(mCurrentTofuConfig.SSID); - } - - @Override - public void onCancelled() { -- handleReject(mCurConfig.SSID); -+ if (mCurrentTofuConfig == null) { -+ return; -+ } -+ handleReject(mCurrentTofuConfig.SSID); - } - }, - new WifiThreadRunner(mHandler)); -@@ -460,16 +639,16 @@ public class InsecureEapNetworkHandler { - } - - private void notifyUserForCaCertificate() { -- if (mCurConfig == null) return; -+ if (mCurrentTofuConfig == null) return; - if (useTrustOnFirstUse()) { -- if (null == mPendingCaCert) return; -+ if (null == mPendingRootCaCert) return; - if (null == mPendingServerCert) return; - } - dismissDialogAndNotification(); - - PendingIntent tapPendingIntent; - if (useTrustOnFirstUse()) { -- tapPendingIntent = genCaCertNotifIntent(ACTION_CERT_NOTIF_TAP, mCurConfig.SSID); -+ tapPendingIntent = genCaCertNotifIntent(ACTION_CERT_NOTIF_TAP, mCurrentTofuConfig.SSID); - } else { - Intent openLinkIntent = new Intent(Intent.ACTION_VIEW) - .setData(Uri.parse(mCaCertHelpLink)) -@@ -482,9 +661,10 @@ public class InsecureEapNetworkHandler { - ? mContext.getString(R.string.wifi_ca_cert_notification_title) - : mContext.getString(R.string.wifi_ca_cert_notification_preT_title); - String content = useTrustOnFirstUse() -- ? mContext.getString(R.string.wifi_ca_cert_notification_message, mCurConfig.SSID) -+ ? mContext.getString(R.string.wifi_ca_cert_notification_message, -+ mCurrentTofuConfig.SSID) - : mContext.getString(R.string.wifi_ca_cert_notification_preT_message, -- mCurConfig.SSID); -+ mCurrentTofuConfig.SSID); - Notification.Builder builder = mFacade.makeNotificationBuilder(mContext, - WifiService.NOTIFICATION_NETWORK_ALERTS) - .setSmallIcon(Icon.createWithResource(mContext.getWifiOverlayApkPkgName(), -@@ -502,11 +682,13 @@ public class InsecureEapNetworkHandler { - Notification.Action acceptAction = new Notification.Action.Builder( - null /* icon */, - mContext.getString(R.string.wifi_ca_cert_dialog_preT_continue_text), -- genCaCertNotifIntent(ACTION_CERT_NOTIF_ACCEPT, mCurConfig.SSID)).build(); -+ genCaCertNotifIntent(ACTION_CERT_NOTIF_ACCEPT, mCurrentTofuConfig.SSID)) -+ .build(); - Notification.Action rejectAction = new Notification.Action.Builder( - null /* icon */, - mContext.getString(R.string.wifi_ca_cert_dialog_preT_abort_text), -- genCaCertNotifIntent(ACTION_CERT_NOTIF_REJECT, mCurConfig.SSID)).build(); -+ genCaCertNotifIntent(ACTION_CERT_NOTIF_REJECT, mCurrentTofuConfig.SSID)) -+ .build(); - builder.addAction(rejectAction).addAction(acceptAction); - } - mNotificationManager.notify(SystemMessage.NOTE_SERVER_CA_CERTIFICATE, builder.build()); -@@ -521,18 +703,18 @@ public class InsecureEapNetworkHandler { - } - - private void clearInternalData() { -- mPendingCaCertDepth = -1; -- mPendingCaCert = null; -+ mPendingRootCaCertDepth = -1; -+ mPendingRootCaCert = null; - mPendingServerCert = null; -- mPendingCaCertSubjectInfo = null; -- mPendingCaCertIssuerInfo = null; -- mCurConfig = null; -+ mPendingServerCertSubjectInfo = null; -+ mPendingServerCertIssuerInfo = null; -+ mCurrentTofuConfig = null; - } - - private void clearNativeData() { - // PMK should be cleared or it would skip EAP flow next time. -- if (null != mCurConfig) { -- mWifiNative.removeNetworkCachedData(mCurConfig.networkId); -+ if (null != mCurrentTofuConfig) { -+ mWifiNative.removeNetworkCachedData(mCurrentTofuConfig.networkId); - } - // remove network so that supplicant's PMKSA cache is cleared - mWifiNative.removeAllNetworks(mInterfaceName); -@@ -551,13 +733,13 @@ public class InsecureEapNetworkHandler { - // If condition #2 occurs, clear existing data and notify the client mode - // via onError callback. - private boolean isConnectionValid(@Nullable String ssid) { -- if (TextUtils.isEmpty(ssid) || null == mCurConfig) { -+ if (TextUtils.isEmpty(ssid) || null == mCurrentTofuConfig) { - handleError(null); - return false; - } - -- if (!TextUtils.equals(ssid, mCurConfig.SSID)) { -- Log.w(TAG, "Target SSID " + mCurConfig.SSID -+ if (!TextUtils.equals(ssid, mCurrentTofuConfig.SSID)) { -+ Log.w(TAG, "Target SSID " + mCurrentTofuConfig.SSID - + " is different from TOFU returned SSID" + ssid); - return false; - } -diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java -index 40acc85cc5..b044508eb7 100644 ---- a/service/java/com/android/server/wifi/WifiServiceImpl.java -+++ b/service/java/com/android/server/wifi/WifiServiceImpl.java -@@ -528,9 +528,6 @@ public class WifiServiceImpl extends BaseWifiService { - if (!mWifiConfigManager.loadFromStore()) { - Log.e(TAG, "Failed to load from config store"); - } -- if (!mWifiGlobals.isInsecureEnterpriseConfigurationAllowed()) { -- mWifiConfigManager.updateTrustOnFirstUseFlag(isTrustOnFirstUseSupported()); -- } - mWifiConfigManager.incrementNumRebootsSinceLastUse(); - // config store is read, check if verbose logging is enabled. - enableVerboseLoggingInternal( -@@ -795,6 +792,10 @@ public class WifiServiceImpl extends BaseWifiService { - mLohsSoftApTracker.handleBootCompleted(); - mWifiInjector.getSarManager().handleBootCompleted(); - mIsBootComplete = true; -+ // HW capabilities is ready after boot completion. -+ if (!mWifiGlobals.isInsecureEnterpriseConfigurationAllowed()) { -+ mWifiConfigManager.updateTrustOnFirstUseFlag(isTrustOnFirstUseSupported()); -+ } - }); - } - -diff --git a/service/proto/src/metrics.proto b/service/proto/src/metrics.proto -index 09a596f984..871eb2c750 100644 ---- a/service/proto/src/metrics.proto -+++ b/service/proto/src/metrics.proto -@@ -1047,6 +1047,9 @@ message ConnectionEvent { - - // The reason code if a user rejects this connection. - AUTH_FAILURE_REJECTED_BY_USER = 7; -+ -+ // The reason code if an insecure Enterprise connection requires user's approval -+ DISCONNECTED_USER_APPROVAL_NEEDED = 8; - } - - // Entity that recommended connecting to this network. -diff --git a/service/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java b/service/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java -index ca8f5b031c..e2309e9ce4 100644 ---- a/service/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java -+++ b/service/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java -@@ -7860,11 +7860,18 @@ public class ClientModeImplTest extends WifiBaseTest { - WifiEnterpriseConfig.Eap.TLS, WifiEnterpriseConfig.Phase2.NONE)); - eapTlsConfig.networkId = FRAMEWORK_NETWORK_ID; - eapTlsConfig.SSID = TEST_SSID; -- if (isAtLeastT) { -+ if (isAtLeastT && isTrustOnFirstUseSupported) { - eapTlsConfig.enterpriseConfig.enableTrustOnFirstUse(true); -+ when(mInsecureEapNetworkHandler.prepareConnection(any(WifiConfiguration.class))) -+ .thenReturn(false); -+ } else { -+ when(mInsecureEapNetworkHandler.prepareConnection(any(WifiConfiguration.class))) -+ .thenReturn(true); - } - eapTlsConfig.enterpriseConfig.setCaPath(""); - eapTlsConfig.enterpriseConfig.setDomainSuffixMatch(""); -+ eapTlsConfig.setRandomizedMacAddress(TEST_LOCAL_MAC_ADDRESS); -+ eapTlsConfig.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_AUTO; - - initializeAndAddNetworkAndVerifySuccess(eapTlsConfig); - -@@ -7879,31 +7886,31 @@ public class ClientModeImplTest extends WifiBaseTest { - } - verify(mInsecureEapNetworkHandler).prepareConnection(eq(eapTlsConfig)); - -- mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, -- new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR, -- SupplicantState.ASSOCIATED)); -- mLooper.dispatchAll(); -- - if (isTrustOnFirstUseSupported) { -+ mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, -+ new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR, -+ SupplicantState.ASSOCIATED)); -+ mLooper.dispatchAll(); -+ - mCmi.sendMessage(WifiMonitor.TOFU_ROOT_CA_CERTIFICATE, - FRAMEWORK_NETWORK_ID, 0, FakeKeys.CA_CERT0); - mLooper.dispatchAll(); -- verify(mInsecureEapNetworkHandler).setPendingCertificate( -+ verify(mInsecureEapNetworkHandler).addPendingCertificate( - eq(eapTlsConfig.SSID), eq(0), eq(FakeKeys.CA_CERT0)); -- } - -- mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, -- new NetworkConnectionEventInfo(0, TEST_WIFI_SSID, TEST_BSSID_STR, false)); -- mLooper.dispatchAll(); -- -- mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, -- new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR, -- SupplicantState.COMPLETED)); -- mLooper.dispatchAll(); -+ // Adding a certificate in depth 0 will cause a disconnection when TOFU is supported -+ DisconnectEventInfo disconnectEventInfo = -+ new DisconnectEventInfo(TEST_SSID, TEST_BSSID_STR, 3, true); -+ mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, disconnectEventInfo); -+ mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, -+ new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR, -+ SupplicantState.DISCONNECTED)); -+ mLooper.dispatchAll(); -+ } - - verify(mInsecureEapNetworkHandler).startUserApprovalIfNecessary(eq(isUserSelected)); -- assertEquals("L3ProvisioningState", getCurrentState().getName()); -- -+ // In any case, we end up in the disconnected state -+ assertEquals("DisconnectedState", getCurrentState().getName()); - return eapTlsConfig; - } - -@@ -7919,9 +7926,22 @@ public class ClientModeImplTest extends WifiBaseTest { - - mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onAccept(testConfig.SSID); - mLooper.dispatchAll(); -- injectDhcpSuccess(); -+ verify(mWifiConnectivityManager).forceConnectivityScan(eq(ClientModeImpl.WIFI_WORK_SOURCE)); -+ } -+ -+ /** -+ * Verify logic when Trust On First Use is not supported -+ * - This network is selected by a user. -+ * - Network gets connected -+ */ -+ @Test -+ public void verifyTrustOnFirstUseAcceptWhenConnectByUserNoTofu() throws Exception { -+ assumeTrue(SdkLevel.isAtLeastT()); -+ WifiConfiguration testConfig = setupTrustOnFirstUse(true, false, true); -+ -+ mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onAccept(testConfig.SSID); - mLooper.dispatchAll(); -- assertEquals("L3ConnectedState", getCurrentState().getName()); -+ verify(mWifiConnectivityManager).forceConnectivityScan(eq(ClientModeImpl.WIFI_WORK_SOURCE)); - } - - /** -@@ -7936,7 +7956,13 @@ public class ClientModeImplTest extends WifiBaseTest { - - mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onReject(testConfig.SSID); - mLooper.dispatchAll(); -- verify(mWifiNative).disconnect(eq(WIFI_IFACE_NAME)); -+ verify(mWifiConnectivityManager, never()) -+ .forceConnectivityScan(eq(ClientModeImpl.WIFI_WORK_SOURCE)); -+ verify(mWifiMetrics).endConnectionEvent( -+ any(), eq(WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION), -+ eq(WifiMetricsProto.ConnectionEvent.HLF_NONE), -+ eq(WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN), -+ anyInt()); - } - - /** -@@ -7951,7 +7977,13 @@ public class ClientModeImplTest extends WifiBaseTest { - - mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onError(testConfig.SSID); - mLooper.dispatchAll(); -- verify(mWifiNative).disconnect(eq(WIFI_IFACE_NAME)); -+ verify(mWifiConnectivityManager, never()) -+ .forceConnectivityScan(eq(ClientModeImpl.WIFI_WORK_SOURCE)); -+ verify(mWifiMetrics).endConnectionEvent( -+ any(), eq(WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION), -+ eq(WifiMetricsProto.ConnectionEvent.HLF_NONE), -+ eq(WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN), -+ anyInt()); - } - - /** -@@ -7967,9 +7999,7 @@ public class ClientModeImplTest extends WifiBaseTest { - - mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onAccept(testConfig.SSID); - mLooper.dispatchAll(); -- injectDhcpSuccess(); -- mLooper.dispatchAll(); -- assertEquals("L3ConnectedState", getCurrentState().getName()); -+ verify(mWifiConnectivityManager).forceConnectivityScan(eq(ClientModeImpl.WIFI_WORK_SOURCE)); - } - - /** -@@ -7985,7 +8015,13 @@ public class ClientModeImplTest extends WifiBaseTest { - - mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onReject(testConfig.SSID); - mLooper.dispatchAll(); -- verify(mWifiNative).disconnect(eq(WIFI_IFACE_NAME)); -+ verify(mWifiConnectivityManager, never()) -+ .forceConnectivityScan(eq(ClientModeImpl.WIFI_WORK_SOURCE)); -+ verify(mWifiMetrics).endConnectionEvent( -+ any(), eq(WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION), -+ eq(WifiMetricsProto.ConnectionEvent.HLF_NONE), -+ eq(WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN), -+ anyInt()); - } - - /** -@@ -8000,7 +8036,13 @@ public class ClientModeImplTest extends WifiBaseTest { - - mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onError(testConfig.SSID); - mLooper.dispatchAll(); -- verify(mWifiNative).disconnect(eq(WIFI_IFACE_NAME)); -+ verify(mWifiConnectivityManager, never()) -+ .forceConnectivityScan(eq(ClientModeImpl.WIFI_WORK_SOURCE)); -+ verify(mWifiMetrics).endConnectionEvent( -+ any(), eq(WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION), -+ eq(WifiMetricsProto.ConnectionEvent.HLF_NONE), -+ eq(WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN), -+ anyInt()); - } - - /** -@@ -8015,9 +8057,7 @@ public class ClientModeImplTest extends WifiBaseTest { - - mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onAccept(testConfig.SSID); - mLooper.dispatchAll(); -- injectDhcpSuccess(); -- mLooper.dispatchAll(); -- assertEquals("L3ConnectedState", getCurrentState().getName()); -+ verify(mWifiConnectivityManager).forceConnectivityScan(eq(ClientModeImpl.WIFI_WORK_SOURCE)); - } - - /** -@@ -8032,7 +8072,13 @@ public class ClientModeImplTest extends WifiBaseTest { - - mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onReject(testConfig.SSID); - mLooper.dispatchAll(); -- verify(mWifiNative).disconnect(eq(WIFI_IFACE_NAME)); -+ verify(mWifiConnectivityManager, never()) -+ .forceConnectivityScan(eq(ClientModeImpl.WIFI_WORK_SOURCE)); -+ verify(mWifiMetrics).endConnectionEvent( -+ any(), eq(WifiMetrics.ConnectionEvent.FAILURE_CONNECT_NETWORK_FAILED), -+ eq(WifiMetricsProto.ConnectionEvent.HLF_NONE), -+ eq(WifiMetricsProto.ConnectionEvent.DISCONNECTED_USER_APPROVAL_NEEDED), -+ anyInt()); - } - - /** -@@ -8047,7 +8093,13 @@ public class ClientModeImplTest extends WifiBaseTest { - - mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onError(testConfig.SSID); - mLooper.dispatchAll(); -- verify(mWifiNative).disconnect(eq(WIFI_IFACE_NAME)); -+ verify(mWifiConnectivityManager, never()) -+ .forceConnectivityScan(eq(ClientModeImpl.WIFI_WORK_SOURCE)); -+ verify(mWifiMetrics).endConnectionEvent( -+ any(), eq(WifiMetrics.ConnectionEvent.FAILURE_CONNECT_NETWORK_FAILED), -+ eq(WifiMetricsProto.ConnectionEvent.HLF_NONE), -+ eq(WifiMetricsProto.ConnectionEvent.DISCONNECTED_USER_APPROVAL_NEEDED), -+ anyInt()); - } - - /** -@@ -8061,9 +8113,8 @@ public class ClientModeImplTest extends WifiBaseTest { - WifiConfiguration testConfig = setupLegacyEapNetworkTest(false); - - mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onAccept(testConfig.SSID); -- injectDhcpSuccess(); - mLooper.dispatchAll(); -- assertEquals("L3ConnectedState", getCurrentState().getName()); -+ verify(mWifiConnectivityManager).forceConnectivityScan(eq(ClientModeImpl.WIFI_WORK_SOURCE)); - } - - /** -@@ -8078,9 +8129,14 @@ public class ClientModeImplTest extends WifiBaseTest { - - mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onReject(testConfig.SSID); - mLooper.dispatchAll(); -- - verify(mFrameworkFacade, never()).makeAlertDialogBuilder(any()); -- verify(mWifiNative).disconnect(eq(WIFI_IFACE_NAME)); -+ verify(mWifiConnectivityManager, never()) -+ .forceConnectivityScan(eq(ClientModeImpl.WIFI_WORK_SOURCE)); -+ verify(mWifiMetrics).endConnectionEvent( -+ any(), eq(WifiMetrics.ConnectionEvent.FAILURE_CONNECT_NETWORK_FAILED), -+ eq(WifiMetricsProto.ConnectionEvent.HLF_NONE), -+ eq(WifiMetricsProto.ConnectionEvent.DISCONNECTED_USER_APPROVAL_NEEDED), -+ anyInt()); - } - - /** -@@ -8095,7 +8151,13 @@ public class ClientModeImplTest extends WifiBaseTest { - - mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onError(testConfig.SSID); - mLooper.dispatchAll(); -- verify(mWifiNative).disconnect(eq(WIFI_IFACE_NAME)); -+ verify(mWifiConnectivityManager, never()) -+ .forceConnectivityScan(eq(ClientModeImpl.WIFI_WORK_SOURCE)); -+ verify(mWifiMetrics).endConnectionEvent( -+ any(), eq(WifiMetrics.ConnectionEvent.FAILURE_CONNECT_NETWORK_FAILED), -+ eq(WifiMetricsProto.ConnectionEvent.HLF_NONE), -+ eq(WifiMetricsProto.ConnectionEvent.DISCONNECTED_USER_APPROVAL_NEEDED), -+ anyInt()); - } - - private void setScanResultWithMloInfo() { -diff --git a/service/tests/wifitests/src/com/android/server/wifi/InsecureEapNetworkHandlerTest.java b/service/tests/wifitests/src/com/android/server/wifi/InsecureEapNetworkHandlerTest.java -index aed3753ffc..6e2e67a8a2 100644 ---- a/service/tests/wifitests/src/com/android/server/wifi/InsecureEapNetworkHandlerTest.java -+++ b/service/tests/wifitests/src/com/android/server/wifi/InsecureEapNetworkHandlerTest.java -@@ -17,14 +17,18 @@ - package com.android.server.wifi; - - import static org.junit.Assert.assertEquals; -+import static org.junit.Assert.assertFalse; -+import static org.junit.Assert.assertNotNull; - import static org.junit.Assert.assertTrue; - import static org.junit.Assume.assumeFalse; - import static org.junit.Assume.assumeTrue; - import static org.mockito.Mockito.any; - import static org.mockito.Mockito.anyInt; -+import static org.mockito.Mockito.anyString; - import static org.mockito.Mockito.argThat; - import static org.mockito.Mockito.atLeastOnce; - import static org.mockito.Mockito.eq; -+import static org.mockito.Mockito.mock; - import static org.mockito.Mockito.never; - import static org.mockito.Mockito.spy; - import static org.mockito.Mockito.validateMockitoUsage; -@@ -37,11 +41,16 @@ import android.content.Intent; - import android.net.wifi.WifiConfiguration; - import android.net.wifi.WifiContext; - import android.net.wifi.WifiEnterpriseConfig; -+import android.net.wifi.util.HexEncoding; - import android.os.Handler; -+import android.text.TextUtils; - - import androidx.test.filters.SmallTest; - - import com.android.modules.utils.build.SdkLevel; -+import com.android.server.wifi.util.CertificateSubjectInfo; -+import com.android.server.wifi.util.NativeUtil; -+import com.android.wifi.resources.R; - - import org.junit.After; - import org.junit.Before; -@@ -51,9 +60,13 @@ import org.mockito.ArgumentCaptor; - import org.mockito.Captor; - import org.mockito.Mock; - import org.mockito.MockitoAnnotations; -+import org.mockito.stubbing.Answer; - -+import java.nio.charset.StandardCharsets; - import java.security.cert.X509Certificate; - -+import javax.security.auth.x500.X500Principal; -+ - /** - * Unit tests for {@link com.android.server.wifi.InsecureEapNetworkHandlerTest}. - */ -@@ -65,7 +78,9 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - private static final int ACTION_TAP = 2; - private static final String WIFI_IFACE_NAME = "wlan-test-9"; - private static final int FRAMEWORK_NETWORK_ID = 2; -- private static final String TEST_SSID = "test_ssid"; -+ private static final String TEST_SSID = "\"test_ssid\""; -+ private static final String TEST_IDENTITY = "userid"; -+ private static final String TEST_PASSWORD = "myPassWord!"; - - @Mock WifiContext mContext; - @Mock WifiConfigManager mWifiConfigManager; -@@ -94,11 +109,34 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - when(mContext.getString(anyInt())).thenReturn("TestString"); - when(mContext.getString(anyInt(), any())).thenReturn("TestStringWithArgument"); - when(mContext.getText(anyInt())).thenReturn("TestStr"); -+ when(mContext.getString(eq(R.string.wifi_ca_cert_dialog_message_issuer_name_text), -+ anyString())) -+ .thenAnswer((Answer) invocation -> -+ "Issuer Name:\n" + invocation.getArguments()[1] + "\n\n"); -+ when(mContext.getString(eq(R.string.wifi_ca_cert_dialog_message_server_name_text), -+ anyString())) -+ .thenAnswer((Answer) invocation -> -+ "Server Name:\n" + invocation.getArguments()[1] + "\n\n"); -+ when(mContext.getString(eq(R.string.wifi_ca_cert_dialog_message_organization_text), -+ anyString())) -+ .thenAnswer((Answer) invocation -> -+ "Organization:\n" + invocation.getArguments()[1] + "\n\n"); -+ when(mContext.getString(eq(R.string.wifi_ca_cert_dialog_message_contact_text), -+ anyString())) -+ .thenAnswer((Answer) invocation -> -+ "Contact:\n" + invocation.getArguments()[1] + "\n\n"); -+ when(mContext.getString(eq(R.string.wifi_ca_cert_dialog_message_signature_name_text), -+ anyString())) -+ .thenAnswer((Answer) invocation -> -+ "Signature:\n" + invocation.getArguments()[1] + "\n\n"); - when(mContext.getWifiOverlayApkPkgName()).thenReturn("test.com.android.wifi.resources"); - when(mContext.getResources()).thenReturn(mResources); - when(mWifiDialogManager.createSimpleDialogWithUrl( - any(), any(), any(), anyInt(), anyInt(), any(), any(), any(), any(), any())) - .thenReturn(mTofuAlertDialog); -+ when(mWifiDialogManager.createSimpleDialog( -+ any(), any(), any(), any(), any(), any(), any())) -+ .thenReturn(mTofuAlertDialog); - - when(mFrameworkFacade.makeNotificationBuilder(any(), any())) - .thenReturn(mNotificationBuilder); -@@ -207,6 +245,9 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - setupTest(config, isAtLeastT, isTrustOnFirstUseSupported); - assertTrue(mInsecureEapNetworkHandler.startUserApprovalIfNecessary(isUserSelected)); - verify(mCallbacks).onError(eq(config.SSID)); -+ verify(mWifiConfigManager, atLeastOnce()).updateNetworkSelectionStatus(eq(config.networkId), -+ eq(WifiConfiguration.NetworkSelectionStatus -+ .DISABLED_BY_WIFI_MANAGER)); - } - - /** -@@ -311,9 +352,36 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - isTrustOnFirstUseSupported, isUserSelected, needUserApproval); - } - -+ private X509Certificate generateMockCert(String subject, String issuer, boolean isCa) { -+ X509Certificate mockCert = mock(X509Certificate.class); -+ X500Principal mockSubjectPrincipal = mock(X500Principal.class); -+ when(mockCert.getSubjectX500Principal()).thenReturn(mockSubjectPrincipal); -+ when(mockSubjectPrincipal.getName()).thenReturn("C=TW,ST=Taiwan,L=Taipei" -+ + ",O=" + subject + " Organization" -+ + ",CN=" + subject -+ + ",1.2.840.113549.1.9.1=#1614" + String.valueOf(HexEncoding.encode( -+ (subject + "@email.com").getBytes(StandardCharsets.UTF_8)))); -+ -+ X500Principal mockIssuerX500Principal = mock(X500Principal.class); -+ when(mockCert.getIssuerX500Principal()).thenReturn(mockIssuerX500Principal); -+ when(mockIssuerX500Principal.getName()).thenReturn("C=TW,ST=Taiwan,L=Taipei" -+ + ",O=" + issuer + " Organization" -+ + ",CN=" + issuer -+ + ",1.2.840.113549.1.9.1=#1614" + String.valueOf(HexEncoding.encode( -+ (issuer + "@email.com").getBytes(StandardCharsets.UTF_8)))); -+ -+ when(mockCert.getSignature()).thenReturn(new byte[]{ -+ (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef, -+ (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78, -+ (byte) 0x90, (byte) 0xab, (byte) 0xcd, (byte) 0xef}); -+ -+ when(mockCert.getBasicConstraints()).thenReturn(isCa ? 99 : -1); -+ return mockCert; -+ } -+ - private WifiConfiguration prepareWifiConfiguration(boolean isAtLeastT) { - WifiConfiguration config = spy(WifiConfigurationTestUtil.createEapNetwork( -- WifiEnterpriseConfig.Eap.TLS, WifiEnterpriseConfig.Phase2.NONE)); -+ WifiEnterpriseConfig.Eap.TTLS, WifiEnterpriseConfig.Phase2.MSCHAPV2)); - config.networkId = FRAMEWORK_NETWORK_ID; - config.SSID = TEST_SSID; - if (isAtLeastT) { -@@ -321,6 +389,8 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - } - config.enterpriseConfig.setCaPath(""); - config.enterpriseConfig.setDomainSuffixMatch(""); -+ config.enterpriseConfig.setIdentity(TEST_IDENTITY); -+ config.enterpriseConfig.setPassword(TEST_PASSWORD); - return config; - } - -@@ -338,14 +408,32 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - mWifiNative, - mFrameworkFacade, - mWifiNotificationManager, -- mWifiDialogManager, isTrustOnFirstUseSupported, -+ mWifiDialogManager, -+ isTrustOnFirstUseSupported, - isInsecureEnterpriseConfigurationAllowed, - mCallbacks, - WIFI_IFACE_NAME, - mHandler); - -+ if (isTrustOnFirstUseSupported -+ && (config.enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.TTLS -+ || config.enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.PEAP) -+ && config.enterpriseConfig.getPhase2Method() != WifiEnterpriseConfig.Phase2.NONE) { -+ // Verify that the configuration contains an identity -+ assertEquals(TEST_IDENTITY, config.enterpriseConfig.getIdentity()); -+ assertEquals(TEST_PASSWORD, config.enterpriseConfig.getPassword()); -+ } - mInsecureEapNetworkHandler.prepareConnection(config); - -+ if (isTrustOnFirstUseSupported -+ && (config.enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.TTLS -+ || config.enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.PEAP) -+ && config.enterpriseConfig.getPhase2Method() != WifiEnterpriseConfig.Phase2.NONE) { -+ // Verify identities are cleared -+ assertTrue(TextUtils.isEmpty(config.enterpriseConfig.getIdentity())); -+ assertTrue(TextUtils.isEmpty(config.enterpriseConfig.getPassword())); -+ } -+ - if (isTrustOnFirstUseSupported && config.enterpriseConfig.isTrustOnFirstUseEnabled()) { - verify(mContext, atLeastOnce()).registerReceiver( - mBroadcastReceiverCaptor.capture(), -@@ -379,34 +467,13 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - WifiConfiguration config = prepareWifiConfiguration(isAtLeastT); - setupTest(config, isAtLeastT, isTrustOnFirstUseSupported); - -- mInsecureEapNetworkHandler.setPendingCertificate(config.SSID, 1, FakeKeys.CA_CERT0); -- mInsecureEapNetworkHandler.setPendingCertificate(config.SSID, 0, FakeKeys.CLIENT_CERT); -- -- verifyTrustOnFirstUseFlow(config, ACTION_ACCEPT, isTrustOnFirstUseSupported, -- isUserSelected, needUserApproval, FakeKeys.CA_CERT0, FakeKeys.CLIENT_CERT); -- } -- -- /** -- * Verify Trust On First Use flow with a reversal cert chain -- * - This network is selected by a user. -- * - Accept the connection. -- */ -- @Test -- public void verifyTrustOnFirstUseAcceptWhenConnectByUserWithReversalOrderChain() -- throws Exception { -- assumeTrue(SdkLevel.isAtLeastT()); -- boolean isAtLeastT = true, isTrustOnFirstUseSupported = true, isUserSelected = true; -- boolean needUserApproval = true; -- -- WifiConfiguration config = prepareWifiConfiguration(isAtLeastT); -- setupTest(config, isAtLeastT, isTrustOnFirstUseSupported); -- -- mInsecureEapNetworkHandler.setPendingCertificate(config.SSID, 0, FakeKeys.CLIENT_CERT); -- mInsecureEapNetworkHandler.setPendingCertificate(config.SSID, 1, FakeKeys.CA_CERT1); -- mInsecureEapNetworkHandler.setPendingCertificate(config.SSID, 2, FakeKeys.CA_CERT0); -+ X509Certificate mockCaCert = generateMockCert("ca", "ca", true); -+ X509Certificate mockServerCert = generateMockCert("server", "ca", false); -+ mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 1, mockCaCert); -+ mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 0, mockServerCert); - - verifyTrustOnFirstUseFlow(config, ACTION_ACCEPT, isTrustOnFirstUseSupported, -- isUserSelected, needUserApproval, FakeKeys.CA_CERT0, FakeKeys.CLIENT_CERT); -+ isUserSelected, needUserApproval, mockCaCert, mockServerCert); - } - - /** -@@ -424,10 +491,11 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - WifiConfiguration config = prepareWifiConfiguration(isAtLeastT); - setupTest(config, isAtLeastT, isTrustOnFirstUseSupported); - -- mInsecureEapNetworkHandler.setPendingCertificate(config.SSID, 0, FakeKeys.CA_CERT0); -+ X509Certificate mockSelfSignedCert = generateMockCert("self", "self", false); -+ mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 0, mockSelfSignedCert); - - verifyTrustOnFirstUseFlow(config, ACTION_ACCEPT, isTrustOnFirstUseSupported, -- isUserSelected, needUserApproval, FakeKeys.CA_CERT0, FakeKeys.CA_CERT0); -+ isUserSelected, needUserApproval, mockSelfSignedCert, mockSelfSignedCert); - } - - /** -@@ -448,6 +516,9 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - assertEquals(needUserApproval, - mInsecureEapNetworkHandler.startUserApprovalIfNecessary(isUserSelected)); - verify(mCallbacks).onError(eq(config.SSID)); -+ verify(mWifiConfigManager, atLeastOnce()).updateNetworkSelectionStatus(eq(config.networkId), -+ eq(WifiConfiguration.NetworkSelectionStatus -+ .DISABLED_BY_WIFI_MANAGER)); - } - - /** -@@ -467,11 +538,17 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - config.enterpriseConfig.enableTrustOnFirstUse(false); - setupTest(config, isAtLeastT, isTrustOnFirstUseSupported); - -- mInsecureEapNetworkHandler.setPendingCertificate(config.SSID, 0, FakeKeys.CA_CERT0); -+ mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 1, -+ generateMockCert("ca", "ca", true)); -+ mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 0, -+ generateMockCert("server", "ca", false)); - - assertEquals(needUserApproval, - mInsecureEapNetworkHandler.startUserApprovalIfNecessary(isUserSelected)); - verify(mCallbacks).onError(eq(config.SSID)); -+ verify(mWifiConfigManager, atLeastOnce()).updateNetworkSelectionStatus(eq(config.networkId), -+ eq(WifiConfiguration.NetworkSelectionStatus -+ .DISABLED_BY_WIFI_MANAGER)); - } - - /** -@@ -479,36 +556,161 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - * - TOFU is supported. - * - Insecure EAP network is allowed. - * - TOFU is not enabled -+ * - No user approval is needed. - */ - @Test - public void verifyNoErrorWithTofuDisabledWhenInsecureEapNetworkIsAllowed() - throws Exception { - assumeTrue(SdkLevel.isAtLeastT()); - boolean isAtLeastT = true, isTrustOnFirstUseSupported = true, isUserSelected = true; -- boolean needUserApproval = true, isInsecureEnterpriseConfigurationAllowed = true; -+ boolean needUserApproval = false, isInsecureEnterpriseConfigurationAllowed = true; - - WifiConfiguration config = prepareWifiConfiguration(isAtLeastT); - config.enterpriseConfig.enableTrustOnFirstUse(false); - setupTest(config, isAtLeastT, isTrustOnFirstUseSupported, - isInsecureEnterpriseConfigurationAllowed); - -- mInsecureEapNetworkHandler.setPendingCertificate(config.SSID, 0, FakeKeys.CA_CERT0); -+ mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 1, -+ generateMockCert("ca", "ca", true)); -+ mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 0, -+ generateMockCert("server", "ca", false)); - - assertEquals(needUserApproval, - mInsecureEapNetworkHandler.startUserApprovalIfNecessary(isUserSelected)); - verify(mCallbacks, never()).onError(any()); - } - -+ /** -+ * Verify that it reports errors if the cert chain is headless. -+ */ -+ @Test -+ public void verifyOnErrorWithHeadlessCertChain() throws Exception { -+ assumeTrue(SdkLevel.isAtLeastT()); -+ boolean isAtLeastT = true, isTrustOnFirstUseSupported = true, isUserSelected = true; -+ -+ WifiConfiguration config = prepareWifiConfiguration(isAtLeastT); -+ setupTest(config, isAtLeastT, isTrustOnFirstUseSupported); -+ -+ // Missing root CA cert. -+ mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 0, -+ generateMockCert("server", "ca", false)); -+ -+ assertTrue(mInsecureEapNetworkHandler.startUserApprovalIfNecessary(isUserSelected)); -+ verify(mCallbacks).onError(eq(config.SSID)); -+ verify(mWifiConfigManager, atLeastOnce()).updateNetworkSelectionStatus(eq(config.networkId), -+ eq(WifiConfiguration.NetworkSelectionStatus -+ .DISABLED_BY_WIFI_MANAGER)); -+ } -+ -+ /** -+ * Verify that is reports errors if the server cert issuer does not match the parent subject. -+ */ -+ @Test -+ public void verifyOnErrorWithIncompleteChain() throws Exception { -+ assumeTrue(SdkLevel.isAtLeastT()); -+ boolean isAtLeastT = true, isTrustOnFirstUseSupported = true, isUserSelected = true; -+ -+ WifiConfiguration config = prepareWifiConfiguration(isAtLeastT); -+ setupTest(config, isAtLeastT, isTrustOnFirstUseSupported); -+ -+ X509Certificate mockCaCert = generateMockCert("ca", "ca", true); -+ // Missing intermediate cert. -+ X509Certificate mockServerCert = generateMockCert("server", "intermediate", false); -+ mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 1, mockCaCert); -+ mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 0, mockServerCert); -+ -+ assertTrue(mInsecureEapNetworkHandler.startUserApprovalIfNecessary(isUserSelected)); -+ verify(mCallbacks).onError(eq(config.SSID)); -+ verify(mWifiConfigManager, atLeastOnce()).updateNetworkSelectionStatus(eq(config.networkId), -+ eq(WifiConfiguration.NetworkSelectionStatus -+ .DISABLED_BY_WIFI_MANAGER)); -+ } -+ -+ /** -+ * Verify that setting pending certificate won't crash with no current configuration. -+ */ -+ @Test -+ public void verifySetPendingCertificateNoCrashWithNoConfig() -+ throws Exception { -+ assumeTrue(SdkLevel.isAtLeastT()); -+ mInsecureEapNetworkHandler = new InsecureEapNetworkHandler( -+ mContext, -+ mWifiConfigManager, -+ mWifiNative, -+ mFrameworkFacade, -+ mWifiNotificationManager, -+ mWifiDialogManager, -+ true /* isTrustOnFirstUseSupported */, -+ false /* isInsecureEnterpriseConfigurationAllowed */, -+ mCallbacks, -+ WIFI_IFACE_NAME, -+ mHandler); -+ X509Certificate mockSelfSignedCert = generateMockCert("self", "self", false); -+ mInsecureEapNetworkHandler.addPendingCertificate("NotExist", 0, mockSelfSignedCert); -+ } -+ -+ @Test -+ public void testExistingCertChainIsClearedOnPreparingNewConnection() throws Exception { -+ assumeTrue(SdkLevel.isAtLeastT()); -+ boolean isAtLeastT = true, isTrustOnFirstUseSupported = true, isUserSelected = true; -+ -+ WifiConfiguration config = prepareWifiConfiguration(isAtLeastT); -+ setupTest(config, isAtLeastT, isTrustOnFirstUseSupported); -+ -+ // Missing root CA cert. -+ mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 0, -+ generateMockCert("server", "ca", false)); -+ -+ // The wrong cert chain should be cleared after this call. -+ mInsecureEapNetworkHandler.prepareConnection(config); -+ -+ X509Certificate mockSelfSignedCert = generateMockCert("self", "self", false); -+ mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 0, mockSelfSignedCert); -+ -+ assertTrue(mInsecureEapNetworkHandler.startUserApprovalIfNecessary(isUserSelected)); -+ verify(mCallbacks, never()).onError(any()); -+ } -+ -+ @Test -+ public void verifyUserApprovalIsNotNeededWithDifferentTargetConfig() throws Exception { -+ assumeTrue(SdkLevel.isAtLeastT()); -+ boolean isAtLeastT = true, isTrustOnFirstUseSupported = true, isUserSelected = true; -+ -+ WifiConfiguration config = prepareWifiConfiguration(isAtLeastT); -+ setupTest(config, isAtLeastT, isTrustOnFirstUseSupported); -+ -+ X509Certificate mockSelfSignedCert = generateMockCert("self", "self", false); -+ mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 0, mockSelfSignedCert); -+ -+ // Pass another PSK config which is not the same as the current one. -+ WifiConfiguration pskConfig = WifiConfigurationTestUtil.createPskNetwork(); -+ pskConfig.networkId = FRAMEWORK_NETWORK_ID + 2; -+ mInsecureEapNetworkHandler.prepareConnection(pskConfig); -+ assertFalse(mInsecureEapNetworkHandler.startUserApprovalIfNecessary(isUserSelected)); -+ verify(mCallbacks, never()).onError(any()); -+ -+ // Pass another non-TOFU EAP config which is not the same as the current one. -+ WifiConfiguration anotherEapConfig = spy(WifiConfigurationTestUtil.createEapNetwork( -+ WifiEnterpriseConfig.Eap.SIM, WifiEnterpriseConfig.Phase2.NONE)); -+ anotherEapConfig.networkId = FRAMEWORK_NETWORK_ID + 1; -+ mInsecureEapNetworkHandler.prepareConnection(anotherEapConfig); -+ assertFalse(mInsecureEapNetworkHandler.startUserApprovalIfNecessary(isUserSelected)); -+ verify(mCallbacks, never()).onError(any()); -+ } -+ - private void verifyTrustOnFirstUseFlowWithDefaultCerts(WifiConfiguration config, - int action, boolean isTrustOnFirstUseSupported, boolean isUserSelected, - boolean needUserApproval) throws Exception { -+ X509Certificate mockCaCert = generateMockCert("ca", "ca", true); -+ X509Certificate mockServerCert = generateMockCert("server", "middle", false); - if (isTrustOnFirstUseSupported) { -- mInsecureEapNetworkHandler.setPendingCertificate(config.SSID, 2, FakeKeys.CA_CERT0); -- mInsecureEapNetworkHandler.setPendingCertificate(config.SSID, 1, FakeKeys.CA_CERT1); -- mInsecureEapNetworkHandler.setPendingCertificate(config.SSID, 0, FakeKeys.CLIENT_CERT); -+ mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 2, mockCaCert); -+ mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 1, -+ generateMockCert("middle", "ca", false)); -+ mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 0, mockServerCert); - } - verifyTrustOnFirstUseFlow(config, action, isTrustOnFirstUseSupported, -- isUserSelected, needUserApproval, FakeKeys.CA_CERT0, FakeKeys.CLIENT_CERT); -+ isUserSelected, needUserApproval, mockCaCert, mockServerCert); - } - - private void verifyTrustOnFirstUseFlow(WifiConfiguration config, -@@ -518,12 +720,17 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - assertEquals(needUserApproval, - mInsecureEapNetworkHandler.startUserApprovalIfNecessary(isUserSelected)); - -+ ArgumentCaptor dialogMessageCaptor = ArgumentCaptor.forClass(String.class); - if (isUserSelected) { - ArgumentCaptor dialogCallbackCaptor = - ArgumentCaptor.forClass(WifiDialogManager.SimpleDialogCallback.class); - verify(mWifiDialogManager).createSimpleDialogWithUrl( -- any(), any(), any(), anyInt(), anyInt(), any(), any(), any(), -- dialogCallbackCaptor.capture(), any()); -+ any(), dialogMessageCaptor.capture(), any(), anyInt(), anyInt(), any(), any(), -+ any(), dialogCallbackCaptor.capture(), any()); -+ if (isTrustOnFirstUseSupported) { -+ assertTofuDialogMessage(expectedCaCert, expectedServerCert, -+ dialogMessageCaptor.getValue()); -+ } - if (action == ACTION_ACCEPT) { - dialogCallbackCaptor.getValue().onPositiveButtonClicked(); - } else if (action == ACTION_REJECT) { -@@ -533,6 +740,7 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - verify(mFrameworkFacade, never()).makeAlertDialogBuilder(any()); - verify(mFrameworkFacade).makeNotificationBuilder( - eq(mContext), eq(WifiService.NOTIFICATION_NETWORK_ALERTS)); -+ - // Trust On First Use notification has no accept and reject action buttons. - // It only supports TAP and launch the dialog. - if (isTrustOnFirstUseSupported) { -@@ -543,8 +751,10 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - ArgumentCaptor dialogCallbackCaptor = - ArgumentCaptor.forClass(WifiDialogManager.SimpleDialogCallback.class); - verify(mWifiDialogManager).createSimpleDialogWithUrl( -- any(), any(), any(), anyInt(), anyInt(), any(), any(), any(), -- dialogCallbackCaptor.capture(), any()); -+ any(), dialogMessageCaptor.capture(), any(), anyInt(), anyInt(), any(), -+ any(), any(), dialogCallbackCaptor.capture(), any()); -+ assertTofuDialogMessage(expectedCaCert, expectedServerCert, -+ dialogMessageCaptor.getValue()); - if (action == ACTION_ACCEPT) { - dialogCallbackCaptor.getValue().onPositiveButtonClicked(); - } else if (action == ACTION_REJECT) { -@@ -566,7 +776,8 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - } - - if (action == ACTION_ACCEPT) { -- verify(mWifiConfigManager).allowAutojoin(eq(config.networkId), eq(true)); -+ verify(mWifiConfigManager).updateNetworkSelectionStatus(eq(config.networkId), -+ eq(WifiConfiguration.NetworkSelectionStatus.DISABLED_NONE)); - if (isTrustOnFirstUseSupported) { - verify(mWifiConfigManager).updateCaCertificate( - eq(config.networkId), eq(expectedCaCert), eq(expectedServerCert)); -@@ -576,7 +787,10 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - } - verify(mCallbacks).onAccept(eq(config.SSID)); - } else if (action == ACTION_REJECT) { -- verify(mWifiConfigManager).allowAutojoin(eq(config.networkId), eq(false)); -+ verify(mWifiConfigManager, atLeastOnce()) -+ .updateNetworkSelectionStatus(eq(config.networkId), -+ eq(WifiConfiguration.NetworkSelectionStatus -+ .DISABLED_BY_WIFI_MANAGER)); - verify(mCallbacks).onReject(eq(config.SSID)); - } else if (action == ACTION_TAP) { - verify(mWifiDialogManager).createSimpleDialogWithUrl( -@@ -586,4 +800,44 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - verify(mCallbacks, never()).onError(any()); - } - -+ private void assertTofuDialogMessage( -+ X509Certificate rootCaCert, -+ X509Certificate serverCert, -+ String message) { -+ CertificateSubjectInfo serverCertSubjectInfo = -+ CertificateSubjectInfo.parse(serverCert.getSubjectX500Principal().getName()); -+ CertificateSubjectInfo serverCertIssuerInfo = -+ CertificateSubjectInfo.parse(serverCert.getIssuerX500Principal().getName()); -+ assertNotNull("Server cert subject info is null", serverCertSubjectInfo); -+ assertNotNull("Server cert issuer info is null", serverCertIssuerInfo); -+ -+ assertTrue("TOFU dialog message does not contain server cert subject name ", -+ message.contains(serverCertSubjectInfo.commonName)); -+ assertTrue("TOFU dialog message does not contain server cert issuer name", -+ message.contains(serverCertIssuerInfo.commonName)); -+ if (!TextUtils.isEmpty(serverCertSubjectInfo.organization)) { -+ assertTrue("TOFU dialog message does not contain server cert organization", -+ message.contains(serverCertSubjectInfo.organization)); -+ } -+ if (!TextUtils.isEmpty(serverCertSubjectInfo.email)) { -+ assertTrue("TOFU dialog message does not contain server cert email", -+ message.contains(serverCertSubjectInfo.email)); -+ } -+ assertTrue("TOFU dialog message does not contain server cert signature", -+ message.contains(NativeUtil.hexStringFromByteArray( -+ rootCaCert.getSignature()).substring(0, 16))); -+ } -+ -+ @Test -+ public void testCleanUp() throws Exception { -+ assumeTrue(SdkLevel.isAtLeastT()); -+ -+ boolean isAtLeastT = true, isTrustOnFirstUseSupported = true; -+ WifiConfiguration config = prepareWifiConfiguration(isAtLeastT); -+ setupTest(config, isAtLeastT, isTrustOnFirstUseSupported); -+ -+ BroadcastReceiver br = mBroadcastReceiverCaptor.getValue(); -+ mInsecureEapNetworkHandler.cleanup(); -+ verify(mContext).unregisterReceiver(br); -+ } - } diff --git a/Patches/LineageOS-20.0/android_packages_modules_Wifi/ASB-2023-06/0002-Revert-TOFU-Don-t-send-credentials-in-an-unauthentic.patch b/Patches/LineageOS-20.0/android_packages_modules_Wifi/ASB-2023-06/0002-Revert-TOFU-Don-t-send-credentials-in-an-unauthentic.patch deleted file mode 100644 index f3cd5ea6..00000000 --- a/Patches/LineageOS-20.0/android_packages_modules_Wifi/ASB-2023-06/0002-Revert-TOFU-Don-t-send-credentials-in-an-unauthentic.patch +++ /dev/null @@ -1,1685 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Hai Shalom -Date: Fri, 3 Feb 2023 00:18:32 +0000 -Subject: [PATCH 2/3] Revert "[TOFU] Don't send credentials in an - unauthenticated TLS tunnel" - -This reverts commit fb630e6a13189321d0e83037adc9e7b30dc6d796. - -Reason for revert: Regression - -Bug ID: 250574778 -(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:a50b83a25a16749bb22552a6d473c3ca11e9d904) -Merged-In: I2788772c8f2f863f0f2117235483b1dd57ab4ae6 -Change-Id: I2788772c8f2f863f0f2117235483b1dd57ab4ae6 ---- - .../res/values/overlayable.xml | 24 -- - .../res/values/strings.xml | 3 - - .../android/server/wifi/ClientModeImpl.java | 56 +-- - .../wifi/InsecureEapNetworkHandler.java | 394 +++++------------- - .../android/server/wifi/WifiServiceImpl.java | 7 +- - service/proto/src/metrics.proto | 3 - - .../server/wifi/ClientModeImplTest.java | 136 ++---- - .../wifi/InsecureEapNetworkHandlerTest.java | 342 ++------------- - 8 files changed, 220 insertions(+), 745 deletions(-) - -diff --git a/service/ServiceWifiResources/res/values/overlayable.xml b/service/ServiceWifiResources/res/values/overlayable.xml -index 75c90a3887..2eef3f5156 100644 ---- a/service/ServiceWifiResources/res/values/overlayable.xml -+++ b/service/ServiceWifiResources/res/values/overlayable.xml -@@ -321,30 +321,6 @@ - - - -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - - - -diff --git a/service/ServiceWifiResources/res/values/strings.xml b/service/ServiceWifiResources/res/values/strings.xml -index db331b2e7b..15ffcf0ada 100644 ---- a/service/ServiceWifiResources/res/values/strings.xml -+++ b/service/ServiceWifiResources/res/values/strings.xml -@@ -205,9 +205,6 @@ - Network needs to be verified - Review network details for %1$s before connecting. Tap to continue. - Certificate installation failed. -- Can\'t connect to %1$s -- The server certificate chain is invalid. -- OK - - - Can\'t verify this network -diff --git a/service/java/com/android/server/wifi/ClientModeImpl.java b/service/java/com/android/server/wifi/ClientModeImpl.java -index 9ec12bdad8..931374bccb 100644 ---- a/service/java/com/android/server/wifi/ClientModeImpl.java -+++ b/service/java/com/android/server/wifi/ClientModeImpl.java -@@ -580,6 +580,9 @@ public class ClientModeImpl extends StateMachine implements ClientMode { - @VisibleForTesting - static final int CMD_ACCEPT_EAP_SERVER_CERTIFICATE = BASE + 301; - -+ @VisibleForTesting -+ static final int CMD_REJECT_EAP_SERVER_CERTIFICATE = BASE + 302; -+ - /* Tracks if suspend optimizations need to be disabled by DHCP, - * screen or due to high perf mode. - * When any of them needs to disable it, we keep the suspend optimizations -@@ -834,11 +837,17 @@ public class ClientModeImpl extends StateMachine implements ClientMode { - @Override - public void onReject(String ssid) { - log("Reject Root CA cert for " + ssid); -+ sendMessage(CMD_REJECT_EAP_SERVER_CERTIFICATE, -+ WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_REJECTED_BY_USER, -+ 0, ssid); - } - - @Override - public void onError(String ssid) { - log("Insecure EAP network error for " + ssid); -+ sendMessage(CMD_REJECT_EAP_SERVER_CERTIFICATE, -+ WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_EAP_FAILURE, -+ 0, ssid); - }}; - mInsecureEapNetworkHandler = new InsecureEapNetworkHandler( - mContext, -@@ -2252,6 +2261,8 @@ public class ClientModeImpl extends StateMachine implements ClientMode { - return "CMD_UPDATE_LINKPROPERTIES"; - case CMD_ACCEPT_EAP_SERVER_CERTIFICATE: - return "CMD_ACCEPT_EAP_SERVER_CERTIFICATE"; -+ case CMD_REJECT_EAP_SERVER_CERTIFICATE: -+ return "CMD_REJECT_EAP_SERVER_CERTIFICATE"; - case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT: - return "SUPPLICANT_STATE_CHANGE_EVENT"; - case WifiMonitor.AUTHENTICATION_FAILURE_EVENT: -@@ -4010,21 +4021,8 @@ public class ClientModeImpl extends StateMachine implements ClientMode { - break; - } - } -+ mInsecureEapNetworkHandler.prepareConnection(mTargetWifiConfiguration); - setSelectedRcoiForPasspoint(config); -- if (mInsecureEapNetworkHandler.prepareConnection(mTargetWifiConfiguration)) { -- /* If TOFU is not supported and the user did not approve to connect to an -- insecure network before, do not connect now and instead, display a dialog -- or a notification, and keep network disconnected to avoid sending the -- credentials. -- */ -- mInsecureEapNetworkHandler.startUserApprovalIfNecessary(mIsUserSelected); -- reportConnectionAttemptEnd( -- WifiMetrics.ConnectionEvent.FAILURE_CONNECT_NETWORK_FAILED, -- WifiMetricsProto.ConnectionEvent.HLF_NONE, -- WifiMetricsProto.ConnectionEvent.DISCONNECTED_USER_APPROVAL_NEEDED); -- transitionTo(mDisconnectedState); -- break; -- } - connectToNetwork(config); - break; - } -@@ -4279,6 +4277,7 @@ public class ClientModeImpl extends StateMachine implements ClientMode { - break; - } - case CMD_ACCEPT_EAP_SERVER_CERTIFICATE: -+ case CMD_REJECT_EAP_SERVER_CERTIFICATE: - case CMD_START_ROAM: - case CMD_START_RSSI_MONITORING_OFFLOAD: - case CMD_STOP_RSSI_MONITORING_OFFLOAD: -@@ -5385,16 +5384,11 @@ public class ClientModeImpl extends StateMachine implements ClientMode { - } - case WifiMonitor.TOFU_ROOT_CA_CERTIFICATE: - if (null == mTargetWifiConfiguration) break; -- int certificateDepth = message.arg2; -- if (!mInsecureEapNetworkHandler.addPendingCertificate( -+ if (!mInsecureEapNetworkHandler.setPendingCertificate( - mTargetWifiConfiguration.SSID, message.arg2, - (X509Certificate) message.obj)) { - Log.d(TAG, "Cannot set pending cert."); - } -- // Launch user approval upon receiving the server certificate -- if (certificateDepth == 0) { -- mInsecureEapNetworkHandler.startUserApprovalIfNecessary(mIsUserSelected); -- } - break; - default: { - handleStatus = NOT_HANDLED; -@@ -5873,6 +5867,10 @@ public class ClientModeImpl extends StateMachine implements ClientMode { - class L3ProvisioningState extends State { - @Override - public void enter() { -+ if (mInsecureEapNetworkHandler.startUserApprovalIfNecessary(mIsUserSelected)) { -+ return; -+ } -+ - startL3Provisioning(); - } - -@@ -5892,6 +5890,18 @@ public class ClientModeImpl extends StateMachine implements ClientMode { - handleStatus = NOT_HANDLED; - break; - } -+ case CMD_ACCEPT_EAP_SERVER_CERTIFICATE: -+ startL3Provisioning(); -+ break; -+ case CMD_REJECT_EAP_SERVER_CERTIFICATE: { -+ int l2FailureReason = message.arg1; -+ reportConnectionAttemptEnd( -+ WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION, -+ WifiMetricsProto.ConnectionEvent.HLF_NONE, -+ l2FailureReason); -+ mWifiNative.disconnect(mInterfaceName); -+ break; -+ } - default: { - handleStatus = NOT_HANDLED; - break; -@@ -6411,12 +6421,6 @@ public class ClientModeImpl extends StateMachine implements ClientMode { - } - break; - } -- case CMD_ACCEPT_EAP_SERVER_CERTIFICATE: -- // Got an approval for a TOFU network, trigger a scan to accelerate the -- // auto-connection. -- logd("User accepted TOFU provided certificate"); -- mWifiConnectivityManager.forceConnectivityScan(ClientModeImpl.WIFI_WORK_SOURCE); -- break; - default: { - handleStatus = NOT_HANDLED; - break; -diff --git a/service/java/com/android/server/wifi/InsecureEapNetworkHandler.java b/service/java/com/android/server/wifi/InsecureEapNetworkHandler.java -index b7389952e0..225d01c7e5 100644 ---- a/service/java/com/android/server/wifi/InsecureEapNetworkHandler.java -+++ b/service/java/com/android/server/wifi/InsecureEapNetworkHandler.java -@@ -40,8 +40,6 @@ import com.android.server.wifi.util.NativeUtil; - import com.android.wifi.resources.R; - - import java.security.cert.X509Certificate; --import java.util.ArrayList; --import java.util.List; - - /** This class is used to handle insecure EAP networks. */ - public class InsecureEapNetworkHandler { -@@ -73,27 +71,18 @@ public class InsecureEapNetworkHandler { - private final String mInterfaceName; - private final Handler mHandler; - -- // The latest connecting configuration from the caller, it is updated on calling -- // prepareConnection() always. This is used to ensure that current TOFU config is aligned -- // with the caller connecting config. - @NonNull -- private WifiConfiguration mConnectingConfig = null; -- // The connecting configuration which is a valid TOFU configuration, it is updated -- // only when the connecting configuration is a valid TOFU configuration and used -- // by later TOFU procedure. -- @NonNull -- private WifiConfiguration mCurrentTofuConfig = null; -- private int mPendingRootCaCertDepth = -1; -+ private WifiConfiguration mCurConfig = null; -+ private int mPendingCaCertDepth = -1; - @Nullable -- private X509Certificate mPendingRootCaCert = null; -+ private X509Certificate mPendingCaCert = null; - @Nullable - private X509Certificate mPendingServerCert = null; -- // This is updated on setting a pending server cert. -- private CertificateSubjectInfo mPendingServerCertSubjectInfo = null; -- // This is updated on setting a pending server cert. -- private CertificateSubjectInfo mPendingServerCertIssuerInfo = null; -- // Record the whole server cert chain from Root CA to the server cert. -- private List mServerCertChain = new ArrayList<>(); -+ // This is updated on setting a pending CA cert. -+ private CertificateSubjectInfo mPendingCaCertSubjectInfo = null; -+ // This is updated on setting a pending CA cert. -+ private CertificateSubjectInfo mPendingCaCertIssuerInfo = null; -+ @Nullable - private WifiDialogManager.DialogHandle mTofuAlertDialog = null; - private boolean mIsCertNotificationReceiverRegistered = false; - -@@ -148,17 +137,16 @@ public class InsecureEapNetworkHandler { - * uses Server Cert, without a valid Root CA certificate or user approval. - * - * @param config the running wifi configuration. -- * @return true if user needs to be notified about an insecure network but TOFU is not supported -- * by the device, or false otherwise. - */ -- public boolean prepareConnection(@NonNull WifiConfiguration config) { -- if (null == config) return false; -- mConnectingConfig = config; -+ public void prepareConnection(@NonNull WifiConfiguration config) { -+ if (null == config) return; - -- if (!config.isEnterprise()) return false; -+ if (!config.isEnterprise()) return; - WifiEnterpriseConfig entConfig = config.enterpriseConfig; -- if (!entConfig.isEapMethodServerCertUsed()) return false; -- if (entConfig.hasCaCertificate()) return false; -+ if (!entConfig.isEapMethodServerCertUsed()) return; -+ if (entConfig.hasCaCertificate()) return; -+ -+ clearConnection(); - - Log.d(TAG, "prepareConnection: isTofuSupported=" + mIsTrustOnFirstUseSupported - + ", isInsecureEapNetworkAllowed=" + mIsInsecureEnterpriseConfigurationAllowed -@@ -167,115 +155,71 @@ public class InsecureEapNetworkHandler { - // If TOFU is not supported or insecure EAP network is allowed without TOFU enabled, - // return to skip the dialog if this network is approved before. - if (entConfig.isUserApproveNoCaCert()) { -- if (!mIsTrustOnFirstUseSupported) return false; -+ if (!mIsTrustOnFirstUseSupported) return; - if (mIsInsecureEnterpriseConfigurationAllowed - && !entConfig.isTrustOnFirstUseEnabled()) { -- return false; -+ return; - } - } - -- if (mIsTrustOnFirstUseSupported) { -- /** -- * Clear the user credentials from this copy of the configuration object. -- * Supplicant will start the phase-1 TLS session to acquire the server certificate chain -- * which will be provided to the framework. Then since the callbacks for identity and -- * password requests are not populated, it will fail the connection and disconnect. -- * This will allow the user to review the certificates at their own pace, and a -- * reconnection would automatically take place with full verification of the chain once -- * they approve. -- */ -- if (config.enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.TTLS -- || config.enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.PEAP) { -- config.enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.NONE); -- config.enterpriseConfig.setIdentity(null); -- config.enterpriseConfig.setPassword(null); -- } else if (config.enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.TLS) { -- config.enterpriseConfig.setClientCertificateAlias(null); -- } -- } -- mCurrentTofuConfig = config; -- mServerCertChain.clear(); -- dismissDialogAndNotification(); -+ mCurConfig = config; - registerCertificateNotificationReceiver(); -- if (!mIsTrustOnFirstUseSupported) { -- /** -- * Devices with no TOFU support, do not connect to the network until the user is -- * aware that the network is insecure, and approves the connection. -- */ -- putNetworkOnHold(false); -- } else { -- // Remove cached PMK in the framework and supplicant to avoid skipping the EAP flow. -- clearNativeData(); -- Log.d(TAG, "Remove native cached data and networks for TOFU."); -- } -- return !mIsTrustOnFirstUseSupported; -+ // Remove cached PMK in the framework and supplicant to avoid -+ // skipping the EAP flow. -+ clearNativeData(); -+ Log.d(TAG, "Remove native cached data and networks for TOFU."); - } - -- /** -- * Do necessary clean up on stopping client mode. -- */ -- public void cleanup() { -- dismissDialogAndNotification(); -+ /** Clear data on disconnecting a connection. */ -+ private void clearConnection() { - unregisterCertificateNotificationReceiver(); -+ dismissDialogAndNotification(); - clearInternalData(); - } - - /** -- * Stores a received certificate for later use. -+ * Store the received certifiate for later use. - * - * @param ssid the target network SSID. - * @param depth the depth of this cert. The Root CA should be 0 or - * a positive number, and the server cert is 0. -- * @param cert a certificate from the server. -+ * @param cert the Root CA certificate from the server. - * @return true if the cert is cached; otherwise, false. - */ -- public boolean addPendingCertificate(@NonNull String ssid, int depth, -+ public boolean setPendingCertificate(@NonNull String ssid, int depth, - @NonNull X509Certificate cert) { -- String configProfileKey = mCurrentTofuConfig != null -- ? mCurrentTofuConfig.getProfileKey() : "null"; - Log.d(TAG, "setPendingCertificate: " + "ssid=" + ssid + " depth=" + depth -- + " current config=" + configProfileKey); -+ + " current config=" + mCurConfig); - if (TextUtils.isEmpty(ssid)) return false; -- if (null == mCurrentTofuConfig) return false; -- if (!TextUtils.equals(ssid, mCurrentTofuConfig.SSID)) return false; -+ if (null == mCurConfig) return false; -+ if (!TextUtils.equals(ssid, mCurConfig.SSID)) return false; - if (null == cert) return false; - if (depth < 0) return false; -- -- if (depth == 0) { -- // Disable network selection upon receiving the server certificate -- putNetworkOnHold(true); -- } -- -- if (!mServerCertChain.contains(cert)) { -- mServerCertChain.add(cert); -- } -- - // 0 is the tail, i.e. the server cert. - if (depth == 0 && null == mPendingServerCert) { - mPendingServerCert = cert; - Log.d(TAG, "Pending server certificate: " + mPendingServerCert); -- mPendingServerCertSubjectInfo = CertificateSubjectInfo.parse( -- cert.getSubjectX500Principal().getName()); -- if (null == mPendingServerCertSubjectInfo) { -- Log.e(TAG, "CA cert has no valid subject."); -- return false; -- } -- mPendingServerCertIssuerInfo = CertificateSubjectInfo.parse( -- cert.getIssuerX500Principal().getName()); -- if (null == mPendingServerCertIssuerInfo) { -- Log.e(TAG, "CA cert has no valid issuer."); -- return false; -- } - } -- -- // Root or intermediate cert. -- if (depth < mPendingRootCaCertDepth) { -+ if (depth < mPendingCaCertDepth) { - Log.d(TAG, "Ignore intermediate cert." + cert); - return true; - } -- mPendingRootCaCertDepth = depth; -- mPendingRootCaCert = cert; -- Log.d(TAG, "Pending Root CA certificate: " + mPendingRootCaCert); -+ -+ mPendingCaCertSubjectInfo = CertificateSubjectInfo.parse( -+ cert.getSubjectDN().getName()); -+ if (null == mPendingCaCertSubjectInfo) { -+ Log.e(TAG, "CA cert has no valid subject."); -+ return false; -+ } -+ mPendingCaCertIssuerInfo = CertificateSubjectInfo.parse( -+ cert.getIssuerDN().getName()); -+ if (null == mPendingCaCertIssuerInfo) { -+ Log.e(TAG, "CA cert has no valid issuer."); -+ return false; -+ } -+ mPendingCaCertDepth = depth; -+ mPendingCaCert = cert; -+ Log.d(TAG, "Pending Root CA certificate: " + mPendingCaCert); - return true; - } - -@@ -300,89 +244,31 @@ public class InsecureEapNetworkHandler { - * @return true if the user approval is needed; otherwise, false. - */ - public boolean startUserApprovalIfNecessary(boolean isUserSelected) { -- if (null == mConnectingConfig || null == mCurrentTofuConfig) return false; -- if (mConnectingConfig.networkId != mCurrentTofuConfig.networkId) return false; -+ if (null == mCurConfig) return false; -+ if (!mCurConfig.isEnterprise()) return false; -+ WifiEnterpriseConfig entConfig = mCurConfig.enterpriseConfig; -+ if (!entConfig.isEapMethodServerCertUsed()) return false; -+ if (entConfig.hasCaCertificate()) return false; - - // If Trust On First Use is supported and insecure enterprise configuration - // is not allowed, TOFU must be used for an Enterprise network without certs. - if (mIsTrustOnFirstUseSupported && !mIsInsecureEnterpriseConfigurationAllowed -- && !mCurrentTofuConfig.enterpriseConfig.isTrustOnFirstUseEnabled()) { -+ && !mCurConfig.enterpriseConfig.isTrustOnFirstUseEnabled()) { - Log.d(TAG, "Trust On First Use is not enabled."); -- handleError(mCurrentTofuConfig.SSID); -+ handleError(mCurConfig.SSID); - return true; - } - - if (useTrustOnFirstUse()) { -- if (null == mPendingRootCaCert) { -- Log.e(TAG, "No valid CA cert for TLS-based connection."); -- handleError(mCurrentTofuConfig.SSID); -+ if (null == mPendingCaCert) { -+ Log.d(TAG, "No valid CA cert for TLS-based connection."); -+ handleError(mCurConfig.SSID); - return true; - } else if (null == mPendingServerCert) { -- Log.e(TAG, "No valid Server cert for TLS-based connection."); -- handleError(mCurrentTofuConfig.SSID); -- return true; -- } else if (!isServerCertChainValid()) { -- Log.e(TAG, "Server cert chain is invalid."); -- String title = mContext.getString(R.string.wifi_tofu_invalid_cert_chain_title, -- mCurrentTofuConfig.SSID); -- String message = mContext.getString(R.string.wifi_tofu_invalid_cert_chain_message); -- String okButtonText = mContext.getString( -- R.string.wifi_tofu_invalid_cert_chain_ok_text); -- -- handleError(mCurrentTofuConfig.SSID); -- -- if (TextUtils.isEmpty(title) || TextUtils.isEmpty(message)) return true; -- -- if (isUserSelected) { -- mTofuAlertDialog = mWifiDialogManager.createSimpleDialog( -- title, -- message, -- null /* positiveButtonText */, -- null /* negativeButtonText */, -- okButtonText, -- new WifiDialogManager.SimpleDialogCallback() { -- @Override -- public void onPositiveButtonClicked() { -- // Not used. -- } -- -- @Override -- public void onNegativeButtonClicked() { -- // Not used. -- } -- -- @Override -- public void onNeutralButtonClicked() { -- // Not used. -- } -- -- @Override -- public void onCancelled() { -- // Not used. -- } -- }, -- new WifiThreadRunner(mHandler)); -- mTofuAlertDialog.launchDialog(); -- } else { -- Notification.Builder builder = mFacade.makeNotificationBuilder(mContext, -- WifiService.NOTIFICATION_NETWORK_ALERTS) -- .setSmallIcon( -- Icon.createWithResource(mContext.getWifiOverlayApkPkgName(), -- com.android.wifi.resources.R -- .drawable.stat_notify_wifi_in_range)) -- .setContentTitle(title) -- .setContentText(message) -- .setStyle(new Notification.BigTextStyle().bigText(message)) -- .setColor(mContext.getResources().getColor( -- android.R.color.system_notification_accent_color)); -- mNotificationManager.notify(SystemMessage.NOTE_SERVER_CA_CERTIFICATE, -- builder.build()); -- } -+ Log.d(TAG, "No valid Server cert for TLS-based connection."); -+ handleError(mCurConfig.SSID); - return true; - } -- } else if (mIsInsecureEnterpriseConfigurationAllowed) { -- Log.i(TAG, "networks without the server cert are allowed, skip it."); -- return false; - } - - Log.d(TAG, "startUserApprovalIfNecessaryForInsecureEapNetwork: mIsUserSelected=" -@@ -396,56 +282,13 @@ public class InsecureEapNetworkHandler { - return true; - } - -- /** -- * Disable network selection, disconnect if necessary, and clear PMK cache -- */ -- private void putNetworkOnHold(boolean needToDisconnect) { -- // Disable network selection upon receiving the server certificate -- mWifiConfigManager.updateNetworkSelectionStatus(mCurrentTofuConfig.networkId, -- WifiConfiguration.NetworkSelectionStatus -- .DISABLED_BY_WIFI_MANAGER); -- -- // Force disconnect and clear PMK cache to avoid supplicant reconnection -- if (needToDisconnect) mWifiNative.disconnect(mInterfaceName); -- clearNativeData(); -- } -- -- private boolean isServerCertChainValid() { -- if (mServerCertChain.size() == 0) return false; -- -- X509Certificate parentCert = null; -- for (X509Certificate cert: mServerCertChain) { -- String subject = cert.getSubjectX500Principal().getName(); -- String issuer = cert.getIssuerX500Principal().getName(); -- boolean isCa = cert.getBasicConstraints() >= 0; -- Log.d(TAG, "Subject: " + subject + ", Issuer: " + issuer + ", isCA: " + isCa); -- -- if (parentCert == null) { -- // The root cert, it should be a CA cert or a self-signed cert. -- if (!isCa && !subject.equals(issuer)) { -- Log.e(TAG, "The root cert is not a CA cert or a self-signed cert."); -- return false; -- } -- } else { -- // The issuer of intermediate cert of the leaf cert should be -- // the same as the subject of its parent cert. -- if (!parentCert.getSubjectX500Principal().getName().equals(issuer)) { -- Log.e(TAG, "The issuer does not match the subject of its parent."); -- return false; -- } -- } -- parentCert = cert; -- } -- return true; -- } -- - private boolean useTrustOnFirstUse() { - return mIsTrustOnFirstUseSupported -- && mCurrentTofuConfig.enterpriseConfig.isTrustOnFirstUseEnabled(); -+ && mCurConfig.enterpriseConfig.isTrustOnFirstUseEnabled(); - } - - private void registerCertificateNotificationReceiver() { -- unregisterCertificateNotificationReceiver(); -+ if (mIsCertNotificationReceiverRegistered) return; - - IntentFilter filter = new IntentFilter(); - if (useTrustOnFirstUse()) { -@@ -470,22 +313,21 @@ public class InsecureEapNetworkHandler { - if (!isConnectionValid(ssid)) return; - - if (!useTrustOnFirstUse()) { -- mWifiConfigManager.setUserApproveNoCaCert(mCurrentTofuConfig.networkId, true); -+ mWifiConfigManager.setUserApproveNoCaCert(mCurConfig.networkId, true); - } else { -- if (null == mPendingRootCaCert || null == mPendingServerCert) { -+ if (null == mPendingCaCert || null == mPendingServerCert) { - handleError(ssid); - return; - } - if (!mWifiConfigManager.updateCaCertificate( -- mCurrentTofuConfig.networkId, mPendingRootCaCert, mPendingServerCert)) { -+ mCurConfig.networkId, mPendingCaCert, mPendingServerCert)) { - // The user approved this network, - // keep the connection regardless of the result. -- Log.e(TAG, "Cannot update CA cert to network " + mCurrentTofuConfig.getProfileKey() -- + ", CA cert = " + mPendingRootCaCert); -+ Log.e(TAG, "Cannot update CA cert to network " + mCurConfig.getProfileKey() -+ + ", CA cert = " + mPendingCaCert); - } - } -- mWifiConfigManager.updateNetworkSelectionStatus(mCurrentTofuConfig.networkId, -- WifiConfiguration.NetworkSelectionStatus.DISABLED_NONE); -+ mWifiConfigManager.allowAutojoin(mCurConfig.networkId, true); - dismissDialogAndNotification(); - clearInternalData(); - -@@ -496,8 +338,7 @@ public class InsecureEapNetworkHandler { - void handleReject(@NonNull String ssid) { - if (!isConnectionValid(ssid)) return; - -- mWifiConfigManager.updateNetworkSelectionStatus(mCurrentTofuConfig.networkId, -- WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER); -+ mWifiConfigManager.allowAutojoin(mCurConfig.networkId, false); - dismissDialogAndNotification(); - clearInternalData(); - clearNativeData(); -@@ -506,11 +347,6 @@ public class InsecureEapNetworkHandler { - } - - private void handleError(@Nullable String ssid) { -- if (mCurrentTofuConfig != null) { -- mWifiConfigManager.updateNetworkSelectionStatus(mCurrentTofuConfig.networkId, -- WifiConfiguration.NetworkSelectionStatus -- .DISABLED_BY_WIFI_MANAGER); -- } - dismissDialogAndNotification(); - clearInternalData(); - clearNativeData(); -@@ -519,9 +355,9 @@ public class InsecureEapNetworkHandler { - } - - private void askForUserApprovalForCaCertificate() { -- if (mCurrentTofuConfig == null || TextUtils.isEmpty(mCurrentTofuConfig.SSID)) return; -+ if (mCurConfig == null || TextUtils.isEmpty(mCurConfig.SSID)) return; - if (useTrustOnFirstUse()) { -- if (null == mPendingRootCaCert || null == mPendingServerCert) { -+ if (null == mPendingCaCert || null == mPendingServerCert) { - Log.e(TAG, "Cannot launch a dialog for TOFU without " - + "a valid pending CA certificate."); - return; -@@ -539,42 +375,39 @@ public class InsecureEapNetworkHandler { - ? mContext.getString(R.string.wifi_ca_cert_dialog_abort_text) - : mContext.getString(R.string.wifi_ca_cert_dialog_preT_abort_text); - -- String message; -+ String message = null; - String messageUrl = null; - int messageUrlStart = 0; - int messageUrlEnd = 0; - if (useTrustOnFirstUse()) { -+ String signature = NativeUtil.hexStringFromByteArray( -+ mPendingCaCert.getSignature()); - StringBuilder contentBuilder = new StringBuilder() - .append(mContext.getString(R.string.wifi_ca_cert_dialog_message_hint)) - .append(mContext.getString( - R.string.wifi_ca_cert_dialog_message_server_name_text, -- mPendingServerCertSubjectInfo.commonName)) -+ mPendingCaCertSubjectInfo.commonName)) - .append(mContext.getString( - R.string.wifi_ca_cert_dialog_message_issuer_name_text, -- mPendingServerCertIssuerInfo.commonName)); -- if (!TextUtils.isEmpty(mPendingServerCertSubjectInfo.organization)) { -+ mPendingCaCertIssuerInfo.commonName)); -+ if (!TextUtils.isEmpty(mPendingCaCertSubjectInfo.organization)) { - contentBuilder.append(mContext.getString( - R.string.wifi_ca_cert_dialog_message_organization_text, -- mPendingServerCertSubjectInfo.organization)); -+ mPendingCaCertSubjectInfo.organization)); - } -- if (!TextUtils.isEmpty(mPendingServerCertSubjectInfo.email)) { -+ if (!TextUtils.isEmpty(mPendingCaCertSubjectInfo.email)) { - contentBuilder.append(mContext.getString( - R.string.wifi_ca_cert_dialog_message_contact_text, -- mPendingServerCertSubjectInfo.email)); -- } -- byte[] signature = mPendingServerCert.getSignature(); -- if (signature != null) { -- String signatureString = NativeUtil.hexStringFromByteArray(signature); -- if (signatureString.length() > 16) { -- signatureString = signatureString.substring(0, 16); -- } -- contentBuilder.append(mContext.getString( -- R.string.wifi_ca_cert_dialog_message_signature_name_text, signatureString)); -+ mPendingCaCertSubjectInfo.email)); - } -+ contentBuilder -+ .append(mContext.getString( -+ R.string.wifi_ca_cert_dialog_message_signature_name_text, -+ signature.substring(0, 16))); - message = contentBuilder.toString(); - } else { - String hint = mContext.getString( -- R.string.wifi_ca_cert_dialog_preT_message_hint, mCurrentTofuConfig.SSID); -+ R.string.wifi_ca_cert_dialog_preT_message_hint, mCurConfig.SSID); - String linkText = mContext.getString( - R.string.wifi_ca_cert_dialog_preT_message_link); - message = hint + " " + linkText; -@@ -594,35 +427,23 @@ public class InsecureEapNetworkHandler { - new WifiDialogManager.SimpleDialogCallback() { - @Override - public void onPositiveButtonClicked() { -- if (mCurrentTofuConfig == null) { -- return; -- } -- handleAccept(mCurrentTofuConfig.SSID); -+ handleAccept(mCurConfig.SSID); - } - - @Override - public void onNegativeButtonClicked() { -- if (mCurrentTofuConfig == null) { -- return; -- } -- handleReject(mCurrentTofuConfig.SSID); -+ handleReject(mCurConfig.SSID); - } - - @Override - public void onNeutralButtonClicked() { - // Not used. -- if (mCurrentTofuConfig == null) { -- return; -- } -- handleReject(mCurrentTofuConfig.SSID); -+ handleReject(mCurConfig.SSID); - } - - @Override - public void onCancelled() { -- if (mCurrentTofuConfig == null) { -- return; -- } -- handleReject(mCurrentTofuConfig.SSID); -+ handleReject(mCurConfig.SSID); - } - }, - new WifiThreadRunner(mHandler)); -@@ -639,16 +460,16 @@ public class InsecureEapNetworkHandler { - } - - private void notifyUserForCaCertificate() { -- if (mCurrentTofuConfig == null) return; -+ if (mCurConfig == null) return; - if (useTrustOnFirstUse()) { -- if (null == mPendingRootCaCert) return; -+ if (null == mPendingCaCert) return; - if (null == mPendingServerCert) return; - } - dismissDialogAndNotification(); - - PendingIntent tapPendingIntent; - if (useTrustOnFirstUse()) { -- tapPendingIntent = genCaCertNotifIntent(ACTION_CERT_NOTIF_TAP, mCurrentTofuConfig.SSID); -+ tapPendingIntent = genCaCertNotifIntent(ACTION_CERT_NOTIF_TAP, mCurConfig.SSID); - } else { - Intent openLinkIntent = new Intent(Intent.ACTION_VIEW) - .setData(Uri.parse(mCaCertHelpLink)) -@@ -661,10 +482,9 @@ public class InsecureEapNetworkHandler { - ? mContext.getString(R.string.wifi_ca_cert_notification_title) - : mContext.getString(R.string.wifi_ca_cert_notification_preT_title); - String content = useTrustOnFirstUse() -- ? mContext.getString(R.string.wifi_ca_cert_notification_message, -- mCurrentTofuConfig.SSID) -+ ? mContext.getString(R.string.wifi_ca_cert_notification_message, mCurConfig.SSID) - : mContext.getString(R.string.wifi_ca_cert_notification_preT_message, -- mCurrentTofuConfig.SSID); -+ mCurConfig.SSID); - Notification.Builder builder = mFacade.makeNotificationBuilder(mContext, - WifiService.NOTIFICATION_NETWORK_ALERTS) - .setSmallIcon(Icon.createWithResource(mContext.getWifiOverlayApkPkgName(), -@@ -682,13 +502,11 @@ public class InsecureEapNetworkHandler { - Notification.Action acceptAction = new Notification.Action.Builder( - null /* icon */, - mContext.getString(R.string.wifi_ca_cert_dialog_preT_continue_text), -- genCaCertNotifIntent(ACTION_CERT_NOTIF_ACCEPT, mCurrentTofuConfig.SSID)) -- .build(); -+ genCaCertNotifIntent(ACTION_CERT_NOTIF_ACCEPT, mCurConfig.SSID)).build(); - Notification.Action rejectAction = new Notification.Action.Builder( - null /* icon */, - mContext.getString(R.string.wifi_ca_cert_dialog_preT_abort_text), -- genCaCertNotifIntent(ACTION_CERT_NOTIF_REJECT, mCurrentTofuConfig.SSID)) -- .build(); -+ genCaCertNotifIntent(ACTION_CERT_NOTIF_REJECT, mCurConfig.SSID)).build(); - builder.addAction(rejectAction).addAction(acceptAction); - } - mNotificationManager.notify(SystemMessage.NOTE_SERVER_CA_CERTIFICATE, builder.build()); -@@ -703,18 +521,18 @@ public class InsecureEapNetworkHandler { - } - - private void clearInternalData() { -- mPendingRootCaCertDepth = -1; -- mPendingRootCaCert = null; -+ mPendingCaCertDepth = -1; -+ mPendingCaCert = null; - mPendingServerCert = null; -- mPendingServerCertSubjectInfo = null; -- mPendingServerCertIssuerInfo = null; -- mCurrentTofuConfig = null; -+ mPendingCaCertSubjectInfo = null; -+ mPendingCaCertIssuerInfo = null; -+ mCurConfig = null; - } - - private void clearNativeData() { - // PMK should be cleared or it would skip EAP flow next time. -- if (null != mCurrentTofuConfig) { -- mWifiNative.removeNetworkCachedData(mCurrentTofuConfig.networkId); -+ if (null != mCurConfig) { -+ mWifiNative.removeNetworkCachedData(mCurConfig.networkId); - } - // remove network so that supplicant's PMKSA cache is cleared - mWifiNative.removeAllNetworks(mInterfaceName); -@@ -733,13 +551,13 @@ public class InsecureEapNetworkHandler { - // If condition #2 occurs, clear existing data and notify the client mode - // via onError callback. - private boolean isConnectionValid(@Nullable String ssid) { -- if (TextUtils.isEmpty(ssid) || null == mCurrentTofuConfig) { -+ if (TextUtils.isEmpty(ssid) || null == mCurConfig) { - handleError(null); - return false; - } - -- if (!TextUtils.equals(ssid, mCurrentTofuConfig.SSID)) { -- Log.w(TAG, "Target SSID " + mCurrentTofuConfig.SSID -+ if (!TextUtils.equals(ssid, mCurConfig.SSID)) { -+ Log.w(TAG, "Target SSID " + mCurConfig.SSID - + " is different from TOFU returned SSID" + ssid); - return false; - } -diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java -index b044508eb7..40acc85cc5 100644 ---- a/service/java/com/android/server/wifi/WifiServiceImpl.java -+++ b/service/java/com/android/server/wifi/WifiServiceImpl.java -@@ -528,6 +528,9 @@ public class WifiServiceImpl extends BaseWifiService { - if (!mWifiConfigManager.loadFromStore()) { - Log.e(TAG, "Failed to load from config store"); - } -+ if (!mWifiGlobals.isInsecureEnterpriseConfigurationAllowed()) { -+ mWifiConfigManager.updateTrustOnFirstUseFlag(isTrustOnFirstUseSupported()); -+ } - mWifiConfigManager.incrementNumRebootsSinceLastUse(); - // config store is read, check if verbose logging is enabled. - enableVerboseLoggingInternal( -@@ -792,10 +795,6 @@ public class WifiServiceImpl extends BaseWifiService { - mLohsSoftApTracker.handleBootCompleted(); - mWifiInjector.getSarManager().handleBootCompleted(); - mIsBootComplete = true; -- // HW capabilities is ready after boot completion. -- if (!mWifiGlobals.isInsecureEnterpriseConfigurationAllowed()) { -- mWifiConfigManager.updateTrustOnFirstUseFlag(isTrustOnFirstUseSupported()); -- } - }); - } - -diff --git a/service/proto/src/metrics.proto b/service/proto/src/metrics.proto -index 871eb2c750..09a596f984 100644 ---- a/service/proto/src/metrics.proto -+++ b/service/proto/src/metrics.proto -@@ -1047,9 +1047,6 @@ message ConnectionEvent { - - // The reason code if a user rejects this connection. - AUTH_FAILURE_REJECTED_BY_USER = 7; -- -- // The reason code if an insecure Enterprise connection requires user's approval -- DISCONNECTED_USER_APPROVAL_NEEDED = 8; - } - - // Entity that recommended connecting to this network. -diff --git a/service/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java b/service/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java -index e2309e9ce4..ca8f5b031c 100644 ---- a/service/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java -+++ b/service/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java -@@ -7860,18 +7860,11 @@ public class ClientModeImplTest extends WifiBaseTest { - WifiEnterpriseConfig.Eap.TLS, WifiEnterpriseConfig.Phase2.NONE)); - eapTlsConfig.networkId = FRAMEWORK_NETWORK_ID; - eapTlsConfig.SSID = TEST_SSID; -- if (isAtLeastT && isTrustOnFirstUseSupported) { -+ if (isAtLeastT) { - eapTlsConfig.enterpriseConfig.enableTrustOnFirstUse(true); -- when(mInsecureEapNetworkHandler.prepareConnection(any(WifiConfiguration.class))) -- .thenReturn(false); -- } else { -- when(mInsecureEapNetworkHandler.prepareConnection(any(WifiConfiguration.class))) -- .thenReturn(true); - } - eapTlsConfig.enterpriseConfig.setCaPath(""); - eapTlsConfig.enterpriseConfig.setDomainSuffixMatch(""); -- eapTlsConfig.setRandomizedMacAddress(TEST_LOCAL_MAC_ADDRESS); -- eapTlsConfig.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_AUTO; - - initializeAndAddNetworkAndVerifySuccess(eapTlsConfig); - -@@ -7886,31 +7879,31 @@ public class ClientModeImplTest extends WifiBaseTest { - } - verify(mInsecureEapNetworkHandler).prepareConnection(eq(eapTlsConfig)); - -- if (isTrustOnFirstUseSupported) { -- mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, -- new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR, -- SupplicantState.ASSOCIATED)); -- mLooper.dispatchAll(); -+ mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, -+ new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR, -+ SupplicantState.ASSOCIATED)); -+ mLooper.dispatchAll(); - -+ if (isTrustOnFirstUseSupported) { - mCmi.sendMessage(WifiMonitor.TOFU_ROOT_CA_CERTIFICATE, - FRAMEWORK_NETWORK_ID, 0, FakeKeys.CA_CERT0); - mLooper.dispatchAll(); -- verify(mInsecureEapNetworkHandler).addPendingCertificate( -+ verify(mInsecureEapNetworkHandler).setPendingCertificate( - eq(eapTlsConfig.SSID), eq(0), eq(FakeKeys.CA_CERT0)); -- -- // Adding a certificate in depth 0 will cause a disconnection when TOFU is supported -- DisconnectEventInfo disconnectEventInfo = -- new DisconnectEventInfo(TEST_SSID, TEST_BSSID_STR, 3, true); -- mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, disconnectEventInfo); -- mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, -- new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR, -- SupplicantState.DISCONNECTED)); -- mLooper.dispatchAll(); - } - -+ mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, -+ new NetworkConnectionEventInfo(0, TEST_WIFI_SSID, TEST_BSSID_STR, false)); -+ mLooper.dispatchAll(); -+ -+ mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, -+ new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR, -+ SupplicantState.COMPLETED)); -+ mLooper.dispatchAll(); -+ - verify(mInsecureEapNetworkHandler).startUserApprovalIfNecessary(eq(isUserSelected)); -- // In any case, we end up in the disconnected state -- assertEquals("DisconnectedState", getCurrentState().getName()); -+ assertEquals("L3ProvisioningState", getCurrentState().getName()); -+ - return eapTlsConfig; - } - -@@ -7926,22 +7919,9 @@ public class ClientModeImplTest extends WifiBaseTest { - - mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onAccept(testConfig.SSID); - mLooper.dispatchAll(); -- verify(mWifiConnectivityManager).forceConnectivityScan(eq(ClientModeImpl.WIFI_WORK_SOURCE)); -- } -- -- /** -- * Verify logic when Trust On First Use is not supported -- * - This network is selected by a user. -- * - Network gets connected -- */ -- @Test -- public void verifyTrustOnFirstUseAcceptWhenConnectByUserNoTofu() throws Exception { -- assumeTrue(SdkLevel.isAtLeastT()); -- WifiConfiguration testConfig = setupTrustOnFirstUse(true, false, true); -- -- mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onAccept(testConfig.SSID); -+ injectDhcpSuccess(); - mLooper.dispatchAll(); -- verify(mWifiConnectivityManager).forceConnectivityScan(eq(ClientModeImpl.WIFI_WORK_SOURCE)); -+ assertEquals("L3ConnectedState", getCurrentState().getName()); - } - - /** -@@ -7956,13 +7936,7 @@ public class ClientModeImplTest extends WifiBaseTest { - - mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onReject(testConfig.SSID); - mLooper.dispatchAll(); -- verify(mWifiConnectivityManager, never()) -- .forceConnectivityScan(eq(ClientModeImpl.WIFI_WORK_SOURCE)); -- verify(mWifiMetrics).endConnectionEvent( -- any(), eq(WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION), -- eq(WifiMetricsProto.ConnectionEvent.HLF_NONE), -- eq(WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN), -- anyInt()); -+ verify(mWifiNative).disconnect(eq(WIFI_IFACE_NAME)); - } - - /** -@@ -7977,13 +7951,7 @@ public class ClientModeImplTest extends WifiBaseTest { - - mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onError(testConfig.SSID); - mLooper.dispatchAll(); -- verify(mWifiConnectivityManager, never()) -- .forceConnectivityScan(eq(ClientModeImpl.WIFI_WORK_SOURCE)); -- verify(mWifiMetrics).endConnectionEvent( -- any(), eq(WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION), -- eq(WifiMetricsProto.ConnectionEvent.HLF_NONE), -- eq(WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN), -- anyInt()); -+ verify(mWifiNative).disconnect(eq(WIFI_IFACE_NAME)); - } - - /** -@@ -7999,7 +7967,9 @@ public class ClientModeImplTest extends WifiBaseTest { - - mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onAccept(testConfig.SSID); - mLooper.dispatchAll(); -- verify(mWifiConnectivityManager).forceConnectivityScan(eq(ClientModeImpl.WIFI_WORK_SOURCE)); -+ injectDhcpSuccess(); -+ mLooper.dispatchAll(); -+ assertEquals("L3ConnectedState", getCurrentState().getName()); - } - - /** -@@ -8015,13 +7985,7 @@ public class ClientModeImplTest extends WifiBaseTest { - - mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onReject(testConfig.SSID); - mLooper.dispatchAll(); -- verify(mWifiConnectivityManager, never()) -- .forceConnectivityScan(eq(ClientModeImpl.WIFI_WORK_SOURCE)); -- verify(mWifiMetrics).endConnectionEvent( -- any(), eq(WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION), -- eq(WifiMetricsProto.ConnectionEvent.HLF_NONE), -- eq(WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN), -- anyInt()); -+ verify(mWifiNative).disconnect(eq(WIFI_IFACE_NAME)); - } - - /** -@@ -8036,13 +8000,7 @@ public class ClientModeImplTest extends WifiBaseTest { - - mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onError(testConfig.SSID); - mLooper.dispatchAll(); -- verify(mWifiConnectivityManager, never()) -- .forceConnectivityScan(eq(ClientModeImpl.WIFI_WORK_SOURCE)); -- verify(mWifiMetrics).endConnectionEvent( -- any(), eq(WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION), -- eq(WifiMetricsProto.ConnectionEvent.HLF_NONE), -- eq(WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN), -- anyInt()); -+ verify(mWifiNative).disconnect(eq(WIFI_IFACE_NAME)); - } - - /** -@@ -8057,7 +8015,9 @@ public class ClientModeImplTest extends WifiBaseTest { - - mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onAccept(testConfig.SSID); - mLooper.dispatchAll(); -- verify(mWifiConnectivityManager).forceConnectivityScan(eq(ClientModeImpl.WIFI_WORK_SOURCE)); -+ injectDhcpSuccess(); -+ mLooper.dispatchAll(); -+ assertEquals("L3ConnectedState", getCurrentState().getName()); - } - - /** -@@ -8072,13 +8032,7 @@ public class ClientModeImplTest extends WifiBaseTest { - - mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onReject(testConfig.SSID); - mLooper.dispatchAll(); -- verify(mWifiConnectivityManager, never()) -- .forceConnectivityScan(eq(ClientModeImpl.WIFI_WORK_SOURCE)); -- verify(mWifiMetrics).endConnectionEvent( -- any(), eq(WifiMetrics.ConnectionEvent.FAILURE_CONNECT_NETWORK_FAILED), -- eq(WifiMetricsProto.ConnectionEvent.HLF_NONE), -- eq(WifiMetricsProto.ConnectionEvent.DISCONNECTED_USER_APPROVAL_NEEDED), -- anyInt()); -+ verify(mWifiNative).disconnect(eq(WIFI_IFACE_NAME)); - } - - /** -@@ -8093,13 +8047,7 @@ public class ClientModeImplTest extends WifiBaseTest { - - mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onError(testConfig.SSID); - mLooper.dispatchAll(); -- verify(mWifiConnectivityManager, never()) -- .forceConnectivityScan(eq(ClientModeImpl.WIFI_WORK_SOURCE)); -- verify(mWifiMetrics).endConnectionEvent( -- any(), eq(WifiMetrics.ConnectionEvent.FAILURE_CONNECT_NETWORK_FAILED), -- eq(WifiMetricsProto.ConnectionEvent.HLF_NONE), -- eq(WifiMetricsProto.ConnectionEvent.DISCONNECTED_USER_APPROVAL_NEEDED), -- anyInt()); -+ verify(mWifiNative).disconnect(eq(WIFI_IFACE_NAME)); - } - - /** -@@ -8113,8 +8061,9 @@ public class ClientModeImplTest extends WifiBaseTest { - WifiConfiguration testConfig = setupLegacyEapNetworkTest(false); - - mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onAccept(testConfig.SSID); -+ injectDhcpSuccess(); - mLooper.dispatchAll(); -- verify(mWifiConnectivityManager).forceConnectivityScan(eq(ClientModeImpl.WIFI_WORK_SOURCE)); -+ assertEquals("L3ConnectedState", getCurrentState().getName()); - } - - /** -@@ -8129,14 +8078,9 @@ public class ClientModeImplTest extends WifiBaseTest { - - mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onReject(testConfig.SSID); - mLooper.dispatchAll(); -+ - verify(mFrameworkFacade, never()).makeAlertDialogBuilder(any()); -- verify(mWifiConnectivityManager, never()) -- .forceConnectivityScan(eq(ClientModeImpl.WIFI_WORK_SOURCE)); -- verify(mWifiMetrics).endConnectionEvent( -- any(), eq(WifiMetrics.ConnectionEvent.FAILURE_CONNECT_NETWORK_FAILED), -- eq(WifiMetricsProto.ConnectionEvent.HLF_NONE), -- eq(WifiMetricsProto.ConnectionEvent.DISCONNECTED_USER_APPROVAL_NEEDED), -- anyInt()); -+ verify(mWifiNative).disconnect(eq(WIFI_IFACE_NAME)); - } - - /** -@@ -8151,13 +8095,7 @@ public class ClientModeImplTest extends WifiBaseTest { - - mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onError(testConfig.SSID); - mLooper.dispatchAll(); -- verify(mWifiConnectivityManager, never()) -- .forceConnectivityScan(eq(ClientModeImpl.WIFI_WORK_SOURCE)); -- verify(mWifiMetrics).endConnectionEvent( -- any(), eq(WifiMetrics.ConnectionEvent.FAILURE_CONNECT_NETWORK_FAILED), -- eq(WifiMetricsProto.ConnectionEvent.HLF_NONE), -- eq(WifiMetricsProto.ConnectionEvent.DISCONNECTED_USER_APPROVAL_NEEDED), -- anyInt()); -+ verify(mWifiNative).disconnect(eq(WIFI_IFACE_NAME)); - } - - private void setScanResultWithMloInfo() { -diff --git a/service/tests/wifitests/src/com/android/server/wifi/InsecureEapNetworkHandlerTest.java b/service/tests/wifitests/src/com/android/server/wifi/InsecureEapNetworkHandlerTest.java -index 6e2e67a8a2..aed3753ffc 100644 ---- a/service/tests/wifitests/src/com/android/server/wifi/InsecureEapNetworkHandlerTest.java -+++ b/service/tests/wifitests/src/com/android/server/wifi/InsecureEapNetworkHandlerTest.java -@@ -17,18 +17,14 @@ - package com.android.server.wifi; - - import static org.junit.Assert.assertEquals; --import static org.junit.Assert.assertFalse; --import static org.junit.Assert.assertNotNull; - import static org.junit.Assert.assertTrue; - import static org.junit.Assume.assumeFalse; - import static org.junit.Assume.assumeTrue; - import static org.mockito.Mockito.any; - import static org.mockito.Mockito.anyInt; --import static org.mockito.Mockito.anyString; - import static org.mockito.Mockito.argThat; - import static org.mockito.Mockito.atLeastOnce; - import static org.mockito.Mockito.eq; --import static org.mockito.Mockito.mock; - import static org.mockito.Mockito.never; - import static org.mockito.Mockito.spy; - import static org.mockito.Mockito.validateMockitoUsage; -@@ -41,16 +37,11 @@ import android.content.Intent; - import android.net.wifi.WifiConfiguration; - import android.net.wifi.WifiContext; - import android.net.wifi.WifiEnterpriseConfig; --import android.net.wifi.util.HexEncoding; - import android.os.Handler; --import android.text.TextUtils; - - import androidx.test.filters.SmallTest; - - import com.android.modules.utils.build.SdkLevel; --import com.android.server.wifi.util.CertificateSubjectInfo; --import com.android.server.wifi.util.NativeUtil; --import com.android.wifi.resources.R; - - import org.junit.After; - import org.junit.Before; -@@ -60,13 +51,9 @@ import org.mockito.ArgumentCaptor; - import org.mockito.Captor; - import org.mockito.Mock; - import org.mockito.MockitoAnnotations; --import org.mockito.stubbing.Answer; - --import java.nio.charset.StandardCharsets; - import java.security.cert.X509Certificate; - --import javax.security.auth.x500.X500Principal; -- - /** - * Unit tests for {@link com.android.server.wifi.InsecureEapNetworkHandlerTest}. - */ -@@ -78,9 +65,7 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - private static final int ACTION_TAP = 2; - private static final String WIFI_IFACE_NAME = "wlan-test-9"; - private static final int FRAMEWORK_NETWORK_ID = 2; -- private static final String TEST_SSID = "\"test_ssid\""; -- private static final String TEST_IDENTITY = "userid"; -- private static final String TEST_PASSWORD = "myPassWord!"; -+ private static final String TEST_SSID = "test_ssid"; - - @Mock WifiContext mContext; - @Mock WifiConfigManager mWifiConfigManager; -@@ -109,34 +94,11 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - when(mContext.getString(anyInt())).thenReturn("TestString"); - when(mContext.getString(anyInt(), any())).thenReturn("TestStringWithArgument"); - when(mContext.getText(anyInt())).thenReturn("TestStr"); -- when(mContext.getString(eq(R.string.wifi_ca_cert_dialog_message_issuer_name_text), -- anyString())) -- .thenAnswer((Answer) invocation -> -- "Issuer Name:\n" + invocation.getArguments()[1] + "\n\n"); -- when(mContext.getString(eq(R.string.wifi_ca_cert_dialog_message_server_name_text), -- anyString())) -- .thenAnswer((Answer) invocation -> -- "Server Name:\n" + invocation.getArguments()[1] + "\n\n"); -- when(mContext.getString(eq(R.string.wifi_ca_cert_dialog_message_organization_text), -- anyString())) -- .thenAnswer((Answer) invocation -> -- "Organization:\n" + invocation.getArguments()[1] + "\n\n"); -- when(mContext.getString(eq(R.string.wifi_ca_cert_dialog_message_contact_text), -- anyString())) -- .thenAnswer((Answer) invocation -> -- "Contact:\n" + invocation.getArguments()[1] + "\n\n"); -- when(mContext.getString(eq(R.string.wifi_ca_cert_dialog_message_signature_name_text), -- anyString())) -- .thenAnswer((Answer) invocation -> -- "Signature:\n" + invocation.getArguments()[1] + "\n\n"); - when(mContext.getWifiOverlayApkPkgName()).thenReturn("test.com.android.wifi.resources"); - when(mContext.getResources()).thenReturn(mResources); - when(mWifiDialogManager.createSimpleDialogWithUrl( - any(), any(), any(), anyInt(), anyInt(), any(), any(), any(), any(), any())) - .thenReturn(mTofuAlertDialog); -- when(mWifiDialogManager.createSimpleDialog( -- any(), any(), any(), any(), any(), any(), any())) -- .thenReturn(mTofuAlertDialog); - - when(mFrameworkFacade.makeNotificationBuilder(any(), any())) - .thenReturn(mNotificationBuilder); -@@ -245,9 +207,6 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - setupTest(config, isAtLeastT, isTrustOnFirstUseSupported); - assertTrue(mInsecureEapNetworkHandler.startUserApprovalIfNecessary(isUserSelected)); - verify(mCallbacks).onError(eq(config.SSID)); -- verify(mWifiConfigManager, atLeastOnce()).updateNetworkSelectionStatus(eq(config.networkId), -- eq(WifiConfiguration.NetworkSelectionStatus -- .DISABLED_BY_WIFI_MANAGER)); - } - - /** -@@ -352,36 +311,9 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - isTrustOnFirstUseSupported, isUserSelected, needUserApproval); - } - -- private X509Certificate generateMockCert(String subject, String issuer, boolean isCa) { -- X509Certificate mockCert = mock(X509Certificate.class); -- X500Principal mockSubjectPrincipal = mock(X500Principal.class); -- when(mockCert.getSubjectX500Principal()).thenReturn(mockSubjectPrincipal); -- when(mockSubjectPrincipal.getName()).thenReturn("C=TW,ST=Taiwan,L=Taipei" -- + ",O=" + subject + " Organization" -- + ",CN=" + subject -- + ",1.2.840.113549.1.9.1=#1614" + String.valueOf(HexEncoding.encode( -- (subject + "@email.com").getBytes(StandardCharsets.UTF_8)))); -- -- X500Principal mockIssuerX500Principal = mock(X500Principal.class); -- when(mockCert.getIssuerX500Principal()).thenReturn(mockIssuerX500Principal); -- when(mockIssuerX500Principal.getName()).thenReturn("C=TW,ST=Taiwan,L=Taipei" -- + ",O=" + issuer + " Organization" -- + ",CN=" + issuer -- + ",1.2.840.113549.1.9.1=#1614" + String.valueOf(HexEncoding.encode( -- (issuer + "@email.com").getBytes(StandardCharsets.UTF_8)))); -- -- when(mockCert.getSignature()).thenReturn(new byte[]{ -- (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef, -- (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78, -- (byte) 0x90, (byte) 0xab, (byte) 0xcd, (byte) 0xef}); -- -- when(mockCert.getBasicConstraints()).thenReturn(isCa ? 99 : -1); -- return mockCert; -- } -- - private WifiConfiguration prepareWifiConfiguration(boolean isAtLeastT) { - WifiConfiguration config = spy(WifiConfigurationTestUtil.createEapNetwork( -- WifiEnterpriseConfig.Eap.TTLS, WifiEnterpriseConfig.Phase2.MSCHAPV2)); -+ WifiEnterpriseConfig.Eap.TLS, WifiEnterpriseConfig.Phase2.NONE)); - config.networkId = FRAMEWORK_NETWORK_ID; - config.SSID = TEST_SSID; - if (isAtLeastT) { -@@ -389,8 +321,6 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - } - config.enterpriseConfig.setCaPath(""); - config.enterpriseConfig.setDomainSuffixMatch(""); -- config.enterpriseConfig.setIdentity(TEST_IDENTITY); -- config.enterpriseConfig.setPassword(TEST_PASSWORD); - return config; - } - -@@ -408,32 +338,14 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - mWifiNative, - mFrameworkFacade, - mWifiNotificationManager, -- mWifiDialogManager, -- isTrustOnFirstUseSupported, -+ mWifiDialogManager, isTrustOnFirstUseSupported, - isInsecureEnterpriseConfigurationAllowed, - mCallbacks, - WIFI_IFACE_NAME, - mHandler); - -- if (isTrustOnFirstUseSupported -- && (config.enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.TTLS -- || config.enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.PEAP) -- && config.enterpriseConfig.getPhase2Method() != WifiEnterpriseConfig.Phase2.NONE) { -- // Verify that the configuration contains an identity -- assertEquals(TEST_IDENTITY, config.enterpriseConfig.getIdentity()); -- assertEquals(TEST_PASSWORD, config.enterpriseConfig.getPassword()); -- } - mInsecureEapNetworkHandler.prepareConnection(config); - -- if (isTrustOnFirstUseSupported -- && (config.enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.TTLS -- || config.enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.PEAP) -- && config.enterpriseConfig.getPhase2Method() != WifiEnterpriseConfig.Phase2.NONE) { -- // Verify identities are cleared -- assertTrue(TextUtils.isEmpty(config.enterpriseConfig.getIdentity())); -- assertTrue(TextUtils.isEmpty(config.enterpriseConfig.getPassword())); -- } -- - if (isTrustOnFirstUseSupported && config.enterpriseConfig.isTrustOnFirstUseEnabled()) { - verify(mContext, atLeastOnce()).registerReceiver( - mBroadcastReceiverCaptor.capture(), -@@ -467,13 +379,34 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - WifiConfiguration config = prepareWifiConfiguration(isAtLeastT); - setupTest(config, isAtLeastT, isTrustOnFirstUseSupported); - -- X509Certificate mockCaCert = generateMockCert("ca", "ca", true); -- X509Certificate mockServerCert = generateMockCert("server", "ca", false); -- mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 1, mockCaCert); -- mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 0, mockServerCert); -+ mInsecureEapNetworkHandler.setPendingCertificate(config.SSID, 1, FakeKeys.CA_CERT0); -+ mInsecureEapNetworkHandler.setPendingCertificate(config.SSID, 0, FakeKeys.CLIENT_CERT); -+ -+ verifyTrustOnFirstUseFlow(config, ACTION_ACCEPT, isTrustOnFirstUseSupported, -+ isUserSelected, needUserApproval, FakeKeys.CA_CERT0, FakeKeys.CLIENT_CERT); -+ } -+ -+ /** -+ * Verify Trust On First Use flow with a reversal cert chain -+ * - This network is selected by a user. -+ * - Accept the connection. -+ */ -+ @Test -+ public void verifyTrustOnFirstUseAcceptWhenConnectByUserWithReversalOrderChain() -+ throws Exception { -+ assumeTrue(SdkLevel.isAtLeastT()); -+ boolean isAtLeastT = true, isTrustOnFirstUseSupported = true, isUserSelected = true; -+ boolean needUserApproval = true; -+ -+ WifiConfiguration config = prepareWifiConfiguration(isAtLeastT); -+ setupTest(config, isAtLeastT, isTrustOnFirstUseSupported); -+ -+ mInsecureEapNetworkHandler.setPendingCertificate(config.SSID, 0, FakeKeys.CLIENT_CERT); -+ mInsecureEapNetworkHandler.setPendingCertificate(config.SSID, 1, FakeKeys.CA_CERT1); -+ mInsecureEapNetworkHandler.setPendingCertificate(config.SSID, 2, FakeKeys.CA_CERT0); - - verifyTrustOnFirstUseFlow(config, ACTION_ACCEPT, isTrustOnFirstUseSupported, -- isUserSelected, needUserApproval, mockCaCert, mockServerCert); -+ isUserSelected, needUserApproval, FakeKeys.CA_CERT0, FakeKeys.CLIENT_CERT); - } - - /** -@@ -491,11 +424,10 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - WifiConfiguration config = prepareWifiConfiguration(isAtLeastT); - setupTest(config, isAtLeastT, isTrustOnFirstUseSupported); - -- X509Certificate mockSelfSignedCert = generateMockCert("self", "self", false); -- mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 0, mockSelfSignedCert); -+ mInsecureEapNetworkHandler.setPendingCertificate(config.SSID, 0, FakeKeys.CA_CERT0); - - verifyTrustOnFirstUseFlow(config, ACTION_ACCEPT, isTrustOnFirstUseSupported, -- isUserSelected, needUserApproval, mockSelfSignedCert, mockSelfSignedCert); -+ isUserSelected, needUserApproval, FakeKeys.CA_CERT0, FakeKeys.CA_CERT0); - } - - /** -@@ -516,9 +448,6 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - assertEquals(needUserApproval, - mInsecureEapNetworkHandler.startUserApprovalIfNecessary(isUserSelected)); - verify(mCallbacks).onError(eq(config.SSID)); -- verify(mWifiConfigManager, atLeastOnce()).updateNetworkSelectionStatus(eq(config.networkId), -- eq(WifiConfiguration.NetworkSelectionStatus -- .DISABLED_BY_WIFI_MANAGER)); - } - - /** -@@ -538,17 +467,11 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - config.enterpriseConfig.enableTrustOnFirstUse(false); - setupTest(config, isAtLeastT, isTrustOnFirstUseSupported); - -- mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 1, -- generateMockCert("ca", "ca", true)); -- mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 0, -- generateMockCert("server", "ca", false)); -+ mInsecureEapNetworkHandler.setPendingCertificate(config.SSID, 0, FakeKeys.CA_CERT0); - - assertEquals(needUserApproval, - mInsecureEapNetworkHandler.startUserApprovalIfNecessary(isUserSelected)); - verify(mCallbacks).onError(eq(config.SSID)); -- verify(mWifiConfigManager, atLeastOnce()).updateNetworkSelectionStatus(eq(config.networkId), -- eq(WifiConfiguration.NetworkSelectionStatus -- .DISABLED_BY_WIFI_MANAGER)); - } - - /** -@@ -556,161 +479,36 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - * - TOFU is supported. - * - Insecure EAP network is allowed. - * - TOFU is not enabled -- * - No user approval is needed. - */ - @Test - public void verifyNoErrorWithTofuDisabledWhenInsecureEapNetworkIsAllowed() - throws Exception { - assumeTrue(SdkLevel.isAtLeastT()); - boolean isAtLeastT = true, isTrustOnFirstUseSupported = true, isUserSelected = true; -- boolean needUserApproval = false, isInsecureEnterpriseConfigurationAllowed = true; -+ boolean needUserApproval = true, isInsecureEnterpriseConfigurationAllowed = true; - - WifiConfiguration config = prepareWifiConfiguration(isAtLeastT); - config.enterpriseConfig.enableTrustOnFirstUse(false); - setupTest(config, isAtLeastT, isTrustOnFirstUseSupported, - isInsecureEnterpriseConfigurationAllowed); - -- mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 1, -- generateMockCert("ca", "ca", true)); -- mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 0, -- generateMockCert("server", "ca", false)); -+ mInsecureEapNetworkHandler.setPendingCertificate(config.SSID, 0, FakeKeys.CA_CERT0); - - assertEquals(needUserApproval, - mInsecureEapNetworkHandler.startUserApprovalIfNecessary(isUserSelected)); - verify(mCallbacks, never()).onError(any()); - } - -- /** -- * Verify that it reports errors if the cert chain is headless. -- */ -- @Test -- public void verifyOnErrorWithHeadlessCertChain() throws Exception { -- assumeTrue(SdkLevel.isAtLeastT()); -- boolean isAtLeastT = true, isTrustOnFirstUseSupported = true, isUserSelected = true; -- -- WifiConfiguration config = prepareWifiConfiguration(isAtLeastT); -- setupTest(config, isAtLeastT, isTrustOnFirstUseSupported); -- -- // Missing root CA cert. -- mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 0, -- generateMockCert("server", "ca", false)); -- -- assertTrue(mInsecureEapNetworkHandler.startUserApprovalIfNecessary(isUserSelected)); -- verify(mCallbacks).onError(eq(config.SSID)); -- verify(mWifiConfigManager, atLeastOnce()).updateNetworkSelectionStatus(eq(config.networkId), -- eq(WifiConfiguration.NetworkSelectionStatus -- .DISABLED_BY_WIFI_MANAGER)); -- } -- -- /** -- * Verify that is reports errors if the server cert issuer does not match the parent subject. -- */ -- @Test -- public void verifyOnErrorWithIncompleteChain() throws Exception { -- assumeTrue(SdkLevel.isAtLeastT()); -- boolean isAtLeastT = true, isTrustOnFirstUseSupported = true, isUserSelected = true; -- -- WifiConfiguration config = prepareWifiConfiguration(isAtLeastT); -- setupTest(config, isAtLeastT, isTrustOnFirstUseSupported); -- -- X509Certificate mockCaCert = generateMockCert("ca", "ca", true); -- // Missing intermediate cert. -- X509Certificate mockServerCert = generateMockCert("server", "intermediate", false); -- mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 1, mockCaCert); -- mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 0, mockServerCert); -- -- assertTrue(mInsecureEapNetworkHandler.startUserApprovalIfNecessary(isUserSelected)); -- verify(mCallbacks).onError(eq(config.SSID)); -- verify(mWifiConfigManager, atLeastOnce()).updateNetworkSelectionStatus(eq(config.networkId), -- eq(WifiConfiguration.NetworkSelectionStatus -- .DISABLED_BY_WIFI_MANAGER)); -- } -- -- /** -- * Verify that setting pending certificate won't crash with no current configuration. -- */ -- @Test -- public void verifySetPendingCertificateNoCrashWithNoConfig() -- throws Exception { -- assumeTrue(SdkLevel.isAtLeastT()); -- mInsecureEapNetworkHandler = new InsecureEapNetworkHandler( -- mContext, -- mWifiConfigManager, -- mWifiNative, -- mFrameworkFacade, -- mWifiNotificationManager, -- mWifiDialogManager, -- true /* isTrustOnFirstUseSupported */, -- false /* isInsecureEnterpriseConfigurationAllowed */, -- mCallbacks, -- WIFI_IFACE_NAME, -- mHandler); -- X509Certificate mockSelfSignedCert = generateMockCert("self", "self", false); -- mInsecureEapNetworkHandler.addPendingCertificate("NotExist", 0, mockSelfSignedCert); -- } -- -- @Test -- public void testExistingCertChainIsClearedOnPreparingNewConnection() throws Exception { -- assumeTrue(SdkLevel.isAtLeastT()); -- boolean isAtLeastT = true, isTrustOnFirstUseSupported = true, isUserSelected = true; -- -- WifiConfiguration config = prepareWifiConfiguration(isAtLeastT); -- setupTest(config, isAtLeastT, isTrustOnFirstUseSupported); -- -- // Missing root CA cert. -- mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 0, -- generateMockCert("server", "ca", false)); -- -- // The wrong cert chain should be cleared after this call. -- mInsecureEapNetworkHandler.prepareConnection(config); -- -- X509Certificate mockSelfSignedCert = generateMockCert("self", "self", false); -- mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 0, mockSelfSignedCert); -- -- assertTrue(mInsecureEapNetworkHandler.startUserApprovalIfNecessary(isUserSelected)); -- verify(mCallbacks, never()).onError(any()); -- } -- -- @Test -- public void verifyUserApprovalIsNotNeededWithDifferentTargetConfig() throws Exception { -- assumeTrue(SdkLevel.isAtLeastT()); -- boolean isAtLeastT = true, isTrustOnFirstUseSupported = true, isUserSelected = true; -- -- WifiConfiguration config = prepareWifiConfiguration(isAtLeastT); -- setupTest(config, isAtLeastT, isTrustOnFirstUseSupported); -- -- X509Certificate mockSelfSignedCert = generateMockCert("self", "self", false); -- mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 0, mockSelfSignedCert); -- -- // Pass another PSK config which is not the same as the current one. -- WifiConfiguration pskConfig = WifiConfigurationTestUtil.createPskNetwork(); -- pskConfig.networkId = FRAMEWORK_NETWORK_ID + 2; -- mInsecureEapNetworkHandler.prepareConnection(pskConfig); -- assertFalse(mInsecureEapNetworkHandler.startUserApprovalIfNecessary(isUserSelected)); -- verify(mCallbacks, never()).onError(any()); -- -- // Pass another non-TOFU EAP config which is not the same as the current one. -- WifiConfiguration anotherEapConfig = spy(WifiConfigurationTestUtil.createEapNetwork( -- WifiEnterpriseConfig.Eap.SIM, WifiEnterpriseConfig.Phase2.NONE)); -- anotherEapConfig.networkId = FRAMEWORK_NETWORK_ID + 1; -- mInsecureEapNetworkHandler.prepareConnection(anotherEapConfig); -- assertFalse(mInsecureEapNetworkHandler.startUserApprovalIfNecessary(isUserSelected)); -- verify(mCallbacks, never()).onError(any()); -- } -- - private void verifyTrustOnFirstUseFlowWithDefaultCerts(WifiConfiguration config, - int action, boolean isTrustOnFirstUseSupported, boolean isUserSelected, - boolean needUserApproval) throws Exception { -- X509Certificate mockCaCert = generateMockCert("ca", "ca", true); -- X509Certificate mockServerCert = generateMockCert("server", "middle", false); - if (isTrustOnFirstUseSupported) { -- mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 2, mockCaCert); -- mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 1, -- generateMockCert("middle", "ca", false)); -- mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 0, mockServerCert); -+ mInsecureEapNetworkHandler.setPendingCertificate(config.SSID, 2, FakeKeys.CA_CERT0); -+ mInsecureEapNetworkHandler.setPendingCertificate(config.SSID, 1, FakeKeys.CA_CERT1); -+ mInsecureEapNetworkHandler.setPendingCertificate(config.SSID, 0, FakeKeys.CLIENT_CERT); - } - verifyTrustOnFirstUseFlow(config, action, isTrustOnFirstUseSupported, -- isUserSelected, needUserApproval, mockCaCert, mockServerCert); -+ isUserSelected, needUserApproval, FakeKeys.CA_CERT0, FakeKeys.CLIENT_CERT); - } - - private void verifyTrustOnFirstUseFlow(WifiConfiguration config, -@@ -720,17 +518,12 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - assertEquals(needUserApproval, - mInsecureEapNetworkHandler.startUserApprovalIfNecessary(isUserSelected)); - -- ArgumentCaptor dialogMessageCaptor = ArgumentCaptor.forClass(String.class); - if (isUserSelected) { - ArgumentCaptor dialogCallbackCaptor = - ArgumentCaptor.forClass(WifiDialogManager.SimpleDialogCallback.class); - verify(mWifiDialogManager).createSimpleDialogWithUrl( -- any(), dialogMessageCaptor.capture(), any(), anyInt(), anyInt(), any(), any(), -- any(), dialogCallbackCaptor.capture(), any()); -- if (isTrustOnFirstUseSupported) { -- assertTofuDialogMessage(expectedCaCert, expectedServerCert, -- dialogMessageCaptor.getValue()); -- } -+ any(), any(), any(), anyInt(), anyInt(), any(), any(), any(), -+ dialogCallbackCaptor.capture(), any()); - if (action == ACTION_ACCEPT) { - dialogCallbackCaptor.getValue().onPositiveButtonClicked(); - } else if (action == ACTION_REJECT) { -@@ -740,7 +533,6 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - verify(mFrameworkFacade, never()).makeAlertDialogBuilder(any()); - verify(mFrameworkFacade).makeNotificationBuilder( - eq(mContext), eq(WifiService.NOTIFICATION_NETWORK_ALERTS)); -- - // Trust On First Use notification has no accept and reject action buttons. - // It only supports TAP and launch the dialog. - if (isTrustOnFirstUseSupported) { -@@ -751,10 +543,8 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - ArgumentCaptor dialogCallbackCaptor = - ArgumentCaptor.forClass(WifiDialogManager.SimpleDialogCallback.class); - verify(mWifiDialogManager).createSimpleDialogWithUrl( -- any(), dialogMessageCaptor.capture(), any(), anyInt(), anyInt(), any(), -- any(), any(), dialogCallbackCaptor.capture(), any()); -- assertTofuDialogMessage(expectedCaCert, expectedServerCert, -- dialogMessageCaptor.getValue()); -+ any(), any(), any(), anyInt(), anyInt(), any(), any(), any(), -+ dialogCallbackCaptor.capture(), any()); - if (action == ACTION_ACCEPT) { - dialogCallbackCaptor.getValue().onPositiveButtonClicked(); - } else if (action == ACTION_REJECT) { -@@ -776,8 +566,7 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - } - - if (action == ACTION_ACCEPT) { -- verify(mWifiConfigManager).updateNetworkSelectionStatus(eq(config.networkId), -- eq(WifiConfiguration.NetworkSelectionStatus.DISABLED_NONE)); -+ verify(mWifiConfigManager).allowAutojoin(eq(config.networkId), eq(true)); - if (isTrustOnFirstUseSupported) { - verify(mWifiConfigManager).updateCaCertificate( - eq(config.networkId), eq(expectedCaCert), eq(expectedServerCert)); -@@ -787,10 +576,7 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - } - verify(mCallbacks).onAccept(eq(config.SSID)); - } else if (action == ACTION_REJECT) { -- verify(mWifiConfigManager, atLeastOnce()) -- .updateNetworkSelectionStatus(eq(config.networkId), -- eq(WifiConfiguration.NetworkSelectionStatus -- .DISABLED_BY_WIFI_MANAGER)); -+ verify(mWifiConfigManager).allowAutojoin(eq(config.networkId), eq(false)); - verify(mCallbacks).onReject(eq(config.SSID)); - } else if (action == ACTION_TAP) { - verify(mWifiDialogManager).createSimpleDialogWithUrl( -@@ -800,44 +586,4 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - verify(mCallbacks, never()).onError(any()); - } - -- private void assertTofuDialogMessage( -- X509Certificate rootCaCert, -- X509Certificate serverCert, -- String message) { -- CertificateSubjectInfo serverCertSubjectInfo = -- CertificateSubjectInfo.parse(serverCert.getSubjectX500Principal().getName()); -- CertificateSubjectInfo serverCertIssuerInfo = -- CertificateSubjectInfo.parse(serverCert.getIssuerX500Principal().getName()); -- assertNotNull("Server cert subject info is null", serverCertSubjectInfo); -- assertNotNull("Server cert issuer info is null", serverCertIssuerInfo); -- -- assertTrue("TOFU dialog message does not contain server cert subject name ", -- message.contains(serverCertSubjectInfo.commonName)); -- assertTrue("TOFU dialog message does not contain server cert issuer name", -- message.contains(serverCertIssuerInfo.commonName)); -- if (!TextUtils.isEmpty(serverCertSubjectInfo.organization)) { -- assertTrue("TOFU dialog message does not contain server cert organization", -- message.contains(serverCertSubjectInfo.organization)); -- } -- if (!TextUtils.isEmpty(serverCertSubjectInfo.email)) { -- assertTrue("TOFU dialog message does not contain server cert email", -- message.contains(serverCertSubjectInfo.email)); -- } -- assertTrue("TOFU dialog message does not contain server cert signature", -- message.contains(NativeUtil.hexStringFromByteArray( -- rootCaCert.getSignature()).substring(0, 16))); -- } -- -- @Test -- public void testCleanUp() throws Exception { -- assumeTrue(SdkLevel.isAtLeastT()); -- -- boolean isAtLeastT = true, isTrustOnFirstUseSupported = true; -- WifiConfiguration config = prepareWifiConfiguration(isAtLeastT); -- setupTest(config, isAtLeastT, isTrustOnFirstUseSupported); -- -- BroadcastReceiver br = mBroadcastReceiverCaptor.getValue(); -- mInsecureEapNetworkHandler.cleanup(); -- verify(mContext).unregisterReceiver(br); -- } - } diff --git a/Patches/LineageOS-20.0/android_packages_modules_Wifi/ASB-2023-06/0003-Implement-a-secure-TOFU-flow.patch b/Patches/LineageOS-20.0/android_packages_modules_Wifi/ASB-2023-06/0003-Implement-a-secure-TOFU-flow.patch deleted file mode 100644 index 540148c0..00000000 --- a/Patches/LineageOS-20.0/android_packages_modules_Wifi/ASB-2023-06/0003-Implement-a-secure-TOFU-flow.patch +++ /dev/null @@ -1,2185 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Hai Shalom -Date: Thu, 2 Mar 2023 23:00:56 +0000 -Subject: [PATCH 3/3] Implement a secure TOFU flow - -Implement a secure TOFU flow for supporting devices, and -notifications about insecure connections in non-supporting -devices, when insecure configurations are not allowed. -Handle the case where insecure enterprise configurations are -allowed in the new and secure TOFU flow. In this mode, do not -disconnect the network, do not load certificates, and do not -notify the user about anything. -Display the correct certificate information in the dialog, -remove the email and 8-octet signature from the TOFU dialog, and -replace with user verifiable information: certificate expiration -date (locale adjusted) and a SHA-256 fingerprint of the server -certificate which is locally generated. -Network admins can calculate the fingerprint of their server -certificate and publish the result to their users, using: -openssl x509 -in server-cert.pem -noout -fingerprint -sha256 - -Updated-Overlayable: TRUE -Updated-PDD: TRUE - -Bug: 267633332 -Bug: 251910611 -Bug: 250574778 -Test: atest ClientModeImplTest InsecureEapNetworkHandlerTest -Test: atest WifiConfigManagerTest -Test: Integration test on R, and T devices with overlay setting -of insecure networks allowed and not allowed, and with new -configs and insecure (Do not validate) configs made with R. -Test: Functional test, UI verification with multiple locales -(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:a5227527411bc24e6e2c6276f16559c7305b6783) -Merged-In: I5cac12cd8c52a8a9425e98dad0fb90893f53e374 -Change-Id: I5cac12cd8c52a8a9425e98dad0fb90893f53e374 ---- - .../res/values/overlayable.xml | 25 + - .../res/values/strings.xml | 10 +- - .../android/server/wifi/ClientModeImpl.java | 65 ++- - .../wifi/InsecureEapNetworkHandler.java | 471 +++++++++++++----- - .../server/wifi/WifiConfigManager.java | 32 +- - .../com/android/server/wifi/WifiKeyStore.java | 56 ++- - .../android/server/wifi/WifiServiceImpl.java | 7 +- - service/proto/src/metrics.proto | 3 + - .../server/wifi/ClientModeImplTest.java | 135 +++-- - .../wifi/InsecureEapNetworkHandlerTest.java | 374 +++++++++++--- - .../server/wifi/WifiConfigManagerTest.java | 69 +++ - .../wifi/WifiConfigurationTestUtil.java | 1 + - .../android/server/wifi/WifiKeyStoreTest.java | 31 ++ - 13 files changed, 971 insertions(+), 308 deletions(-) - -diff --git a/service/ServiceWifiResources/res/values/overlayable.xml b/service/ServiceWifiResources/res/values/overlayable.xml -index 2eef3f5156..160b711091 100644 ---- a/service/ServiceWifiResources/res/values/overlayable.xml -+++ b/service/ServiceWifiResources/res/values/overlayable.xml -@@ -321,6 +321,31 @@ - - - -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ - - - -diff --git a/service/ServiceWifiResources/res/values/strings.xml b/service/ServiceWifiResources/res/values/strings.xml -index 15ffcf0ada..bfcb96a1f8 100644 ---- a/service/ServiceWifiResources/res/values/strings.xml -+++ b/service/ServiceWifiResources/res/values/strings.xml -@@ -200,16 +200,20 @@ - Server Name:\n%1$s\n\n - Issuer Name:\n%1$s\n\n - Organization:\n%1$s\n\n -+ Certificate Expiration:\n%1$s\n\n -+ SHA-256 Fingerprint:\n%1$s\n\n - Contact:\n%1$s\n\n -- Signature:\n%1$s\n\n - Network needs to be verified - Review network details for %1$s before connecting. Tap to continue. - Certificate installation failed. -+ Can\'t connect to %1$s -+ The server certificate chain is invalid. -+ OK - - - Can\'t verify this network -- Connect anyway -- Don\'t connect -+ Stay connected -+ Disconnect now - The network %1$s is missing a certificate. - Learn how to add certificates - Can\'t verify this network -diff --git a/service/java/com/android/server/wifi/ClientModeImpl.java b/service/java/com/android/server/wifi/ClientModeImpl.java -index 931374bccb..bd87041319 100644 ---- a/service/java/com/android/server/wifi/ClientModeImpl.java -+++ b/service/java/com/android/server/wifi/ClientModeImpl.java -@@ -581,7 +581,7 @@ public class ClientModeImpl extends StateMachine implements ClientMode { - static final int CMD_ACCEPT_EAP_SERVER_CERTIFICATE = BASE + 301; - - @VisibleForTesting -- static final int CMD_REJECT_EAP_SERVER_CERTIFICATE = BASE + 302; -+ static final int CMD_REJECT_EAP_INSECURE_CONNECTION = BASE + 302; - - /* Tracks if suspend optimizations need to be disabled by DHCP, - * screen or due to high perf mode. -@@ -835,17 +835,17 @@ public class ClientModeImpl extends StateMachine implements ClientMode { - } - - @Override -- public void onReject(String ssid) { -+ public void onReject(String ssid, boolean disconnectRequired) { - log("Reject Root CA cert for " + ssid); -- sendMessage(CMD_REJECT_EAP_SERVER_CERTIFICATE, -+ sendMessage(CMD_REJECT_EAP_INSECURE_CONNECTION, - WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_REJECTED_BY_USER, -- 0, ssid); -+ disconnectRequired ? 1 : 0, ssid); - } - - @Override - public void onError(String ssid) { - log("Insecure EAP network error for " + ssid); -- sendMessage(CMD_REJECT_EAP_SERVER_CERTIFICATE, -+ sendMessage(CMD_REJECT_EAP_INSECURE_CONNECTION, - WifiMetricsProto.ConnectionEvent.AUTH_FAILURE_EAP_FAILURE, - 0, ssid); - }}; -@@ -2261,7 +2261,7 @@ public class ClientModeImpl extends StateMachine implements ClientMode { - return "CMD_UPDATE_LINKPROPERTIES"; - case CMD_ACCEPT_EAP_SERVER_CERTIFICATE: - return "CMD_ACCEPT_EAP_SERVER_CERTIFICATE"; -- case CMD_REJECT_EAP_SERVER_CERTIFICATE: -+ case CMD_REJECT_EAP_INSECURE_CONNECTION: - return "CMD_REJECT_EAP_SERVER_CERTIFICATE"; - case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT: - return "SUPPLICANT_STATE_CHANGE_EVENT"; -@@ -4021,8 +4021,13 @@ public class ClientModeImpl extends StateMachine implements ClientMode { - break; - } - } -- mInsecureEapNetworkHandler.prepareConnection(mTargetWifiConfiguration); - setSelectedRcoiForPasspoint(config); -+ -+ // TOFU flow for devices that do not support this feature -+ mInsecureEapNetworkHandler.prepareConnection(mTargetWifiConfiguration); -+ if (!isTrustOnFirstUseSupported()) { -+ mInsecureEapNetworkHandler.startUserApprovalIfNecessary(mIsUserSelected); -+ } - connectToNetwork(config); - break; - } -@@ -4277,7 +4282,7 @@ public class ClientModeImpl extends StateMachine implements ClientMode { - break; - } - case CMD_ACCEPT_EAP_SERVER_CERTIFICATE: -- case CMD_REJECT_EAP_SERVER_CERTIFICATE: -+ case CMD_REJECT_EAP_INSECURE_CONNECTION: - case CMD_START_ROAM: - case CMD_START_RSSI_MONITORING_OFFLOAD: - case CMD_STOP_RSSI_MONITORING_OFFLOAD: -@@ -5062,6 +5067,18 @@ public class ClientModeImpl extends StateMachine implements ClientMode { - } - break; - } -+ case CMD_REJECT_EAP_INSECURE_CONNECTION: { -+ log("Received CMD_REJECT_EAP_INSECURE_CONNECTION event"); -+ boolean disconnectRequired = message.arg2 == 1; -+ -+ // TOFU connections are not established until the user approves the certificate. -+ // If TOFU is not supported and the network is already connected, this will -+ // disconnect the network. -+ if (disconnectRequired) { -+ sendMessage(CMD_DISCONNECT, StaEvent.DISCONNECT_NETWORK_UNTRUSTED); -+ } -+ break; -+ } - default: { - handleStatus = NOT_HANDLED; - break; -@@ -5384,11 +5401,19 @@ public class ClientModeImpl extends StateMachine implements ClientMode { - } - case WifiMonitor.TOFU_ROOT_CA_CERTIFICATE: - if (null == mTargetWifiConfiguration) break; -- if (!mInsecureEapNetworkHandler.setPendingCertificate( -+ int certificateDepth = message.arg2; -+ if (!mInsecureEapNetworkHandler.addPendingCertificate( - mTargetWifiConfiguration.SSID, message.arg2, - (X509Certificate) message.obj)) { - Log.d(TAG, "Cannot set pending cert."); - } -+ // Launch user approval upon receiving the server certificate and disconnect -+ if (certificateDepth == 0 && mInsecureEapNetworkHandler -+ .startUserApprovalIfNecessary(mIsUserSelected)) { -+ // In the TOFU flow, the user approval dialog is now displayed and the -+ // network remains disconnected and disabled until it is approved. -+ sendMessage(CMD_DISCONNECT, StaEvent.DISCONNECT_NETWORK_UNTRUSTED); -+ } - break; - default: { - handleStatus = NOT_HANDLED; -@@ -5867,10 +5892,6 @@ public class ClientModeImpl extends StateMachine implements ClientMode { - class L3ProvisioningState extends State { - @Override - public void enter() { -- if (mInsecureEapNetworkHandler.startUserApprovalIfNecessary(mIsUserSelected)) { -- return; -- } -- - startL3Provisioning(); - } - -@@ -5890,18 +5911,6 @@ public class ClientModeImpl extends StateMachine implements ClientMode { - handleStatus = NOT_HANDLED; - break; - } -- case CMD_ACCEPT_EAP_SERVER_CERTIFICATE: -- startL3Provisioning(); -- break; -- case CMD_REJECT_EAP_SERVER_CERTIFICATE: { -- int l2FailureReason = message.arg1; -- reportConnectionAttemptEnd( -- WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION, -- WifiMetricsProto.ConnectionEvent.HLF_NONE, -- l2FailureReason); -- mWifiNative.disconnect(mInterfaceName); -- break; -- } - default: { - handleStatus = NOT_HANDLED; - break; -@@ -6421,6 +6430,12 @@ public class ClientModeImpl extends StateMachine implements ClientMode { - } - break; - } -+ case CMD_ACCEPT_EAP_SERVER_CERTIFICATE: -+ // Got an approval for a TOFU network, trigger a scan to accelerate the -+ // auto-connection. -+ logd("User accepted TOFU provided certificate"); -+ mWifiConnectivityManager.forceConnectivityScan(ClientModeImpl.WIFI_WORK_SOURCE); -+ break; - default: { - handleStatus = NOT_HANDLED; - break; -diff --git a/service/java/com/android/server/wifi/InsecureEapNetworkHandler.java b/service/java/com/android/server/wifi/InsecureEapNetworkHandler.java -index 225d01c7e5..6c55feab7f 100644 ---- a/service/java/com/android/server/wifi/InsecureEapNetworkHandler.java -+++ b/service/java/com/android/server/wifi/InsecureEapNetworkHandler.java -@@ -31,15 +31,23 @@ import android.net.wifi.WifiContext; - import android.net.wifi.WifiEnterpriseConfig; - import android.os.Handler; - import android.text.TextUtils; -+import android.text.format.DateFormat; - import android.util.Log; - - import com.android.internal.annotations.VisibleForTesting; - import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; -+import com.android.internal.util.HexDump; - import com.android.server.wifi.util.CertificateSubjectInfo; --import com.android.server.wifi.util.NativeUtil; - import com.android.wifi.resources.R; - -+import java.security.MessageDigest; -+import java.security.NoSuchAlgorithmException; -+import java.security.cert.CertificateEncodingException; - import java.security.cert.X509Certificate; -+import java.util.ArrayList; -+import java.util.Date; -+import java.util.List; -+import java.util.StringJoiner; - - /** This class is used to handle insecure EAP networks. */ - public class InsecureEapNetworkHandler { -@@ -58,6 +66,7 @@ public class InsecureEapNetworkHandler { - static final String EXTRA_PENDING_CERT_SSID = - "com.android.server.wifi.ClientModeImpl.EXTRA_PENDING_CERT_SSID"; - -+ static final String TOFU_ANONYMOUS_IDENTITY = "anonymous"; - private final String mCaCertHelpLink; - private final WifiContext mContext; - private final WifiConfigManager mWifiConfigManager; -@@ -71,18 +80,27 @@ public class InsecureEapNetworkHandler { - private final String mInterfaceName; - private final Handler mHandler; - -+ // The latest connecting configuration from the caller, it is updated on calling -+ // prepareConnection() always. This is used to ensure that current TOFU config is aligned -+ // with the caller connecting config. - @NonNull -- private WifiConfiguration mCurConfig = null; -- private int mPendingCaCertDepth = -1; -+ private WifiConfiguration mConnectingConfig = null; -+ // The connecting configuration which is a valid TOFU configuration, it is updated -+ // only when the connecting configuration is a valid TOFU configuration and used -+ // by later TOFU procedure. -+ @NonNull -+ private WifiConfiguration mCurrentTofuConfig = null; -+ private int mPendingRootCaCertDepth = -1; - @Nullable -- private X509Certificate mPendingCaCert = null; -+ private X509Certificate mPendingRootCaCert = null; - @Nullable - private X509Certificate mPendingServerCert = null; -- // This is updated on setting a pending CA cert. -- private CertificateSubjectInfo mPendingCaCertSubjectInfo = null; -- // This is updated on setting a pending CA cert. -- private CertificateSubjectInfo mPendingCaCertIssuerInfo = null; -- @Nullable -+ // This is updated on setting a pending server cert. -+ private CertificateSubjectInfo mPendingServerCertSubjectInfo = null; -+ // This is updated on setting a pending server cert. -+ private CertificateSubjectInfo mPendingServerCertIssuerInfo = null; -+ // Record the whole server cert chain from Root CA to the server cert. -+ private List mServerCertChain = new ArrayList<>(); - private WifiDialogManager.DialogHandle mTofuAlertDialog = null; - private boolean mIsCertNotificationReceiverRegistered = false; - -@@ -131,29 +149,30 @@ public class InsecureEapNetworkHandler { - } - - /** -- * Prepare data for a new connection. -+ * Prepare TOFU data for a new connection. - * -- * Prepare data if this is an Enterprise configuration, which -+ * Prepare TOFU data if this is an Enterprise configuration, which - * uses Server Cert, without a valid Root CA certificate or user approval. -+ * If TOFU is supported and enabled, this method will also clear the user credentials in the -+ * initial connection to the server. - * - * @param config the running wifi configuration. - */ - public void prepareConnection(@NonNull WifiConfiguration config) { - if (null == config) return; -+ mConnectingConfig = config; - - if (!config.isEnterprise()) return; - WifiEnterpriseConfig entConfig = config.enterpriseConfig; - if (!entConfig.isEapMethodServerCertUsed()) return; - if (entConfig.hasCaCertificate()) return; - -- clearConnection(); -- - Log.d(TAG, "prepareConnection: isTofuSupported=" + mIsTrustOnFirstUseSupported - + ", isInsecureEapNetworkAllowed=" + mIsInsecureEnterpriseConfigurationAllowed - + ", isTofuEnabled=" + entConfig.isTrustOnFirstUseEnabled() - + ", isUserApprovedNoCaCert=" + entConfig.isUserApproveNoCaCert()); - // If TOFU is not supported or insecure EAP network is allowed without TOFU enabled, -- // return to skip the dialog if this network is approved before. -+ // skip the entire TOFU logic if this network was approved earlier by the user. - if (entConfig.isUserApproveNoCaCert()) { - if (!mIsTrustOnFirstUseSupported) return; - if (mIsInsecureEnterpriseConfigurationAllowed -@@ -162,64 +181,122 @@ public class InsecureEapNetworkHandler { - } - } - -- mCurConfig = config; -+ if (mIsTrustOnFirstUseSupported && (entConfig.isTrustOnFirstUseEnabled() -+ || !mIsInsecureEnterpriseConfigurationAllowed)) { -+ /** -+ * Clear the user credentials from this copy of the configuration object. -+ * Supplicant will start the phase-1 TLS session to acquire the server certificate chain -+ * which will be provided to the framework. Then since the callbacks for identity and -+ * password requests are not populated, it will fail the connection and disconnect. -+ * This will allow the user to review the certificates at their own pace, and a -+ * reconnection would automatically take place with full verification of the chain once -+ * they approve. -+ */ -+ if (config.enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.TTLS -+ || config.enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.PEAP) { -+ config.enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.NONE); -+ config.enterpriseConfig.setIdentity(null); -+ if (TextUtils.isEmpty(config.enterpriseConfig.getAnonymousIdentity())) { -+ /** -+ * If anonymous identity was not provided, use "anonymous" to prevent any -+ * untrusted server from tracking real user identities. -+ */ -+ config.enterpriseConfig.setAnonymousIdentity(TOFU_ANONYMOUS_IDENTITY); -+ } -+ config.enterpriseConfig.setPassword(null); -+ } -+ } -+ mCurrentTofuConfig = config; -+ mServerCertChain.clear(); -+ dismissDialogAndNotification(); - registerCertificateNotificationReceiver(); -- // Remove cached PMK in the framework and supplicant to avoid -- // skipping the EAP flow. -- clearNativeData(); -- Log.d(TAG, "Remove native cached data and networks for TOFU."); -+ -+ if (useTrustOnFirstUse()) { -+ // Remove cached PMK in the framework and supplicant to avoid skipping the EAP flow -+ // only when TOFU is in use. -+ clearNativeData(); -+ Log.d(TAG, "Remove native cached data and networks for TOFU."); -+ } - } - -- /** Clear data on disconnecting a connection. */ -- private void clearConnection() { -- unregisterCertificateNotificationReceiver(); -+ /** -+ * Do necessary clean up on stopping client mode. -+ */ -+ public void cleanup() { - dismissDialogAndNotification(); -+ unregisterCertificateNotificationReceiver(); - clearInternalData(); - } - - /** -- * Store the received certifiate for later use. -+ * Stores a received certificate for later use. - * - * @param ssid the target network SSID. - * @param depth the depth of this cert. The Root CA should be 0 or - * a positive number, and the server cert is 0. -- * @param cert the Root CA certificate from the server. -+ * @param cert a certificate from the server. - * @return true if the cert is cached; otherwise, false. - */ -- public boolean setPendingCertificate(@NonNull String ssid, int depth, -+ public boolean addPendingCertificate(@NonNull String ssid, int depth, - @NonNull X509Certificate cert) { -+ String configProfileKey = mCurrentTofuConfig != null -+ ? mCurrentTofuConfig.getProfileKey() : "null"; - Log.d(TAG, "setPendingCertificate: " + "ssid=" + ssid + " depth=" + depth -- + " current config=" + mCurConfig); -+ + " current config=" + configProfileKey); - if (TextUtils.isEmpty(ssid)) return false; -- if (null == mCurConfig) return false; -- if (!TextUtils.equals(ssid, mCurConfig.SSID)) return false; -+ if (null == mCurrentTofuConfig) return false; -+ if (!TextUtils.equals(ssid, mCurrentTofuConfig.SSID)) return false; - if (null == cert) return false; - if (depth < 0) return false; -+ -+ // If TOFU is not supported return immediately, although this should not happen since -+ // the caller code flow is only active when TOFU is supported. -+ if (!mIsTrustOnFirstUseSupported) return false; -+ -+ // If insecure configurations are allowed and this configuration is configured with -+ // "Do not validate" (i.e. TOFU is disabled), skip loading the certificates (no need for -+ // them anyway) and don't disconnect the network. -+ if (mIsInsecureEnterpriseConfigurationAllowed -+ && !mCurrentTofuConfig.enterpriseConfig.isTrustOnFirstUseEnabled()) { -+ Log.d(TAG, "Certificates are not required for this connection"); -+ return false; -+ } -+ -+ if (depth == 0) { -+ // Disable network selection upon receiving the server certificate -+ putNetworkOnHold(); -+ } -+ -+ if (!mServerCertChain.contains(cert)) { -+ mServerCertChain.add(cert); -+ } -+ - // 0 is the tail, i.e. the server cert. - if (depth == 0 && null == mPendingServerCert) { - mPendingServerCert = cert; - Log.d(TAG, "Pending server certificate: " + mPendingServerCert); -+ mPendingServerCertSubjectInfo = CertificateSubjectInfo.parse( -+ cert.getSubjectX500Principal().getName()); -+ if (null == mPendingServerCertSubjectInfo) { -+ Log.e(TAG, "CA cert has no valid subject."); -+ return false; -+ } -+ mPendingServerCertIssuerInfo = CertificateSubjectInfo.parse( -+ cert.getIssuerX500Principal().getName()); -+ if (null == mPendingServerCertIssuerInfo) { -+ Log.e(TAG, "CA cert has no valid issuer."); -+ return false; -+ } - } -- if (depth < mPendingCaCertDepth) { -+ -+ // Root or intermediate cert. -+ if (depth < mPendingRootCaCertDepth) { - Log.d(TAG, "Ignore intermediate cert." + cert); - return true; - } -- -- mPendingCaCertSubjectInfo = CertificateSubjectInfo.parse( -- cert.getSubjectDN().getName()); -- if (null == mPendingCaCertSubjectInfo) { -- Log.e(TAG, "CA cert has no valid subject."); -- return false; -- } -- mPendingCaCertIssuerInfo = CertificateSubjectInfo.parse( -- cert.getIssuerDN().getName()); -- if (null == mPendingCaCertIssuerInfo) { -- Log.e(TAG, "CA cert has no valid issuer."); -- return false; -- } -- mPendingCaCertDepth = depth; -- mPendingCaCert = cert; -- Log.d(TAG, "Pending Root CA certificate: " + mPendingCaCert); -+ mPendingRootCaCertDepth = depth; -+ mPendingRootCaCert = cert; -+ Log.d(TAG, "Pending Root CA certificate: " + mPendingRootCaCert); - return true; - } - -@@ -241,34 +318,42 @@ public class InsecureEapNetworkHandler { - * cert from the server, just mark this network is approved by the user. - * - * @param isUserSelected indicates that this connection is triggered by a user. -- * @return true if the user approval is needed; otherwise, false. -+ * @return true if user approval dialog is displayed and the network is pending. - */ - public boolean startUserApprovalIfNecessary(boolean isUserSelected) { -- if (null == mCurConfig) return false; -- if (!mCurConfig.isEnterprise()) return false; -- WifiEnterpriseConfig entConfig = mCurConfig.enterpriseConfig; -- if (!entConfig.isEapMethodServerCertUsed()) return false; -- if (entConfig.hasCaCertificate()) return false; -+ if (null == mConnectingConfig || null == mCurrentTofuConfig) return false; -+ if (mConnectingConfig.networkId != mCurrentTofuConfig.networkId) return false; - - // If Trust On First Use is supported and insecure enterprise configuration -- // is not allowed, TOFU must be used for an Enterprise network without certs. -+ // is not allowed, TOFU must be used for an Enterprise network without certs. This should -+ // not happen because the TOFU flag will be set during boot if these conditions are met. - if (mIsTrustOnFirstUseSupported && !mIsInsecureEnterpriseConfigurationAllowed -- && !mCurConfig.enterpriseConfig.isTrustOnFirstUseEnabled()) { -- Log.d(TAG, "Trust On First Use is not enabled."); -- handleError(mCurConfig.SSID); -- return true; -+ && !mCurrentTofuConfig.enterpriseConfig.isTrustOnFirstUseEnabled()) { -+ Log.e(TAG, "Upgrade insecure connection to TOFU."); -+ mCurrentTofuConfig.enterpriseConfig.enableTrustOnFirstUse(true); - } - - if (useTrustOnFirstUse()) { -- if (null == mPendingCaCert) { -- Log.d(TAG, "No valid CA cert for TLS-based connection."); -- handleError(mCurConfig.SSID); -- return true; -- } else if (null == mPendingServerCert) { -- Log.d(TAG, "No valid Server cert for TLS-based connection."); -- handleError(mCurConfig.SSID); -- return true; -+ if (null == mPendingRootCaCert) { -+ Log.e(TAG, "No valid CA cert for TLS-based connection."); -+ handleError(mCurrentTofuConfig.SSID); -+ return false; - } -+ if (null == mPendingServerCert) { -+ Log.e(TAG, "No valid Server cert for TLS-based connection."); -+ handleError(mCurrentTofuConfig.SSID); -+ return false; -+ } -+ if (!isServerCertChainValid()) { -+ Log.e(TAG, "Server cert chain is invalid."); -+ String ssid = mCurrentTofuConfig.SSID; -+ handleError(ssid); -+ createCertificateErrorNotification(isUserSelected, ssid); -+ return false; -+ } -+ } else if (mIsInsecureEnterpriseConfigurationAllowed) { -+ Log.i(TAG, "Insecure networks without a Root CA cert are allowed."); -+ return false; - } - - Log.d(TAG, "startUserApprovalIfNecessaryForInsecureEapNetwork: mIsUserSelected=" -@@ -282,13 +367,114 @@ public class InsecureEapNetworkHandler { - return true; - } - -+ /** -+ * Create a notification or a dialog when a server certificate is invalid -+ */ -+ private void createCertificateErrorNotification(boolean isUserSelected, String ssid) { -+ String title = mContext.getString(R.string.wifi_tofu_invalid_cert_chain_title, ssid); -+ String message = mContext.getString(R.string.wifi_tofu_invalid_cert_chain_message); -+ String okButtonText = mContext.getString( -+ R.string.wifi_tofu_invalid_cert_chain_ok_text); -+ -+ if (TextUtils.isEmpty(title) || TextUtils.isEmpty(message)) return; -+ -+ if (isUserSelected) { -+ mTofuAlertDialog = mWifiDialogManager.createSimpleDialog( -+ title, -+ message, -+ null /* positiveButtonText */, -+ null /* negativeButtonText */, -+ okButtonText, -+ new WifiDialogManager.SimpleDialogCallback() { -+ @Override -+ public void onPositiveButtonClicked() { -+ // Not used. -+ } -+ -+ @Override -+ public void onNegativeButtonClicked() { -+ // Not used. -+ } -+ -+ @Override -+ public void onNeutralButtonClicked() { -+ // Not used. -+ } -+ -+ @Override -+ public void onCancelled() { -+ // Not used. -+ } -+ }, -+ new WifiThreadRunner(mHandler)); -+ mTofuAlertDialog.launchDialog(); -+ } else { -+ Notification.Builder builder = mFacade.makeNotificationBuilder(mContext, -+ WifiService.NOTIFICATION_NETWORK_ALERTS) -+ .setSmallIcon( -+ Icon.createWithResource(mContext.getWifiOverlayApkPkgName(), -+ com.android.wifi.resources.R -+ .drawable.stat_notify_wifi_in_range)) -+ .setContentTitle(title) -+ .setContentText(message) -+ .setStyle(new Notification.BigTextStyle().bigText(message)) -+ .setColor(mContext.getResources().getColor( -+ android.R.color.system_notification_accent_color)); -+ mNotificationManager.notify(SystemMessage.NOTE_SERVER_CA_CERTIFICATE, -+ builder.build()); -+ } -+ } -+ -+ /** -+ * Disable network selection, disconnect if necessary, and clear PMK cache -+ */ -+ private void putNetworkOnHold() { -+ // Disable network selection upon receiving the server certificate -+ mWifiConfigManager.updateNetworkSelectionStatus(mCurrentTofuConfig.networkId, -+ WifiConfiguration.NetworkSelectionStatus -+ .DISABLED_BY_WIFI_MANAGER); -+ -+ // Force disconnect and clear PMK cache to avoid supplicant reconnection -+ mWifiNative.disconnect(mInterfaceName); -+ clearNativeData(); -+ } -+ -+ private boolean isServerCertChainValid() { -+ if (mServerCertChain.size() == 0) return false; -+ -+ X509Certificate parentCert = null; -+ for (X509Certificate cert: mServerCertChain) { -+ String subject = cert.getSubjectX500Principal().getName(); -+ String issuer = cert.getIssuerX500Principal().getName(); -+ boolean isCa = cert.getBasicConstraints() >= 0; -+ Log.d(TAG, "Subject: " + subject + ", Issuer: " + issuer + ", isCA: " + isCa); -+ -+ if (parentCert == null) { -+ // The root cert, it should be a CA cert or a self-signed cert. -+ if (!isCa && !subject.equals(issuer)) { -+ Log.e(TAG, "The root cert is not a CA cert or a self-signed cert."); -+ return false; -+ } -+ } else { -+ // The issuer of intermediate cert of the leaf cert should be -+ // the same as the subject of its parent cert. -+ if (!parentCert.getSubjectX500Principal().getName().equals(issuer)) { -+ Log.e(TAG, "The issuer does not match the subject of its parent."); -+ return false; -+ } -+ } -+ parentCert = cert; -+ } -+ return true; -+ } -+ - private boolean useTrustOnFirstUse() { - return mIsTrustOnFirstUseSupported -- && mCurConfig.enterpriseConfig.isTrustOnFirstUseEnabled(); -+ && mCurrentTofuConfig.enterpriseConfig.isTrustOnFirstUseEnabled(); - } - - private void registerCertificateNotificationReceiver() { -- if (mIsCertNotificationReceiverRegistered) return; -+ unregisterCertificateNotificationReceiver(); - - IntentFilter filter = new IntentFilter(); - if (useTrustOnFirstUse()) { -@@ -313,21 +499,22 @@ public class InsecureEapNetworkHandler { - if (!isConnectionValid(ssid)) return; - - if (!useTrustOnFirstUse()) { -- mWifiConfigManager.setUserApproveNoCaCert(mCurConfig.networkId, true); -+ mWifiConfigManager.setUserApproveNoCaCert(mCurrentTofuConfig.networkId, true); - } else { -- if (null == mPendingCaCert || null == mPendingServerCert) { -+ if (null == mPendingRootCaCert || null == mPendingServerCert) { - handleError(ssid); - return; - } - if (!mWifiConfigManager.updateCaCertificate( -- mCurConfig.networkId, mPendingCaCert, mPendingServerCert)) { -+ mCurrentTofuConfig.networkId, mPendingRootCaCert, mPendingServerCert)) { - // The user approved this network, - // keep the connection regardless of the result. -- Log.e(TAG, "Cannot update CA cert to network " + mCurConfig.getProfileKey() -- + ", CA cert = " + mPendingCaCert); -+ Log.e(TAG, "Cannot update CA cert to network " + mCurrentTofuConfig.getProfileKey() -+ + ", CA cert = " + mPendingRootCaCert); - } - } -- mWifiConfigManager.allowAutojoin(mCurConfig.networkId, true); -+ mWifiConfigManager.updateNetworkSelectionStatus(mCurrentTofuConfig.networkId, -+ WifiConfiguration.NetworkSelectionStatus.DISABLED_NONE); - dismissDialogAndNotification(); - clearInternalData(); - -@@ -337,16 +524,22 @@ public class InsecureEapNetworkHandler { - @VisibleForTesting - void handleReject(@NonNull String ssid) { - if (!isConnectionValid(ssid)) return; -+ boolean disconnectRequired = !useTrustOnFirstUse(); - -- mWifiConfigManager.allowAutojoin(mCurConfig.networkId, false); -+ mWifiConfigManager.updateNetworkSelectionStatus(mCurrentTofuConfig.networkId, -+ WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER); - dismissDialogAndNotification(); - clearInternalData(); -- clearNativeData(); -- -- if (null != mCallbacks) mCallbacks.onReject(ssid); -+ if (disconnectRequired) clearNativeData(); -+ if (null != mCallbacks) mCallbacks.onReject(ssid, disconnectRequired); - } - - private void handleError(@Nullable String ssid) { -+ if (mCurrentTofuConfig != null) { -+ mWifiConfigManager.updateNetworkSelectionStatus(mCurrentTofuConfig.networkId, -+ WifiConfiguration.NetworkSelectionStatus -+ .DISABLED_BY_WIFI_MANAGER); -+ } - dismissDialogAndNotification(); - clearInternalData(); - clearNativeData(); -@@ -355,9 +548,9 @@ public class InsecureEapNetworkHandler { - } - - private void askForUserApprovalForCaCertificate() { -- if (mCurConfig == null || TextUtils.isEmpty(mCurConfig.SSID)) return; -+ if (mCurrentTofuConfig == null || TextUtils.isEmpty(mCurrentTofuConfig.SSID)) return; - if (useTrustOnFirstUse()) { -- if (null == mPendingCaCert || null == mPendingServerCert) { -+ if (null == mPendingRootCaCert || null == mPendingServerCert) { - Log.e(TAG, "Cannot launch a dialog for TOFU without " - + "a valid pending CA certificate."); - return; -@@ -375,39 +568,39 @@ public class InsecureEapNetworkHandler { - ? mContext.getString(R.string.wifi_ca_cert_dialog_abort_text) - : mContext.getString(R.string.wifi_ca_cert_dialog_preT_abort_text); - -- String message = null; -+ String message; - String messageUrl = null; - int messageUrlStart = 0; - int messageUrlEnd = 0; - if (useTrustOnFirstUse()) { -- String signature = NativeUtil.hexStringFromByteArray( -- mPendingCaCert.getSignature()); - StringBuilder contentBuilder = new StringBuilder() - .append(mContext.getString(R.string.wifi_ca_cert_dialog_message_hint)) - .append(mContext.getString( - R.string.wifi_ca_cert_dialog_message_server_name_text, -- mPendingCaCertSubjectInfo.commonName)) -+ mPendingServerCertSubjectInfo.commonName)) - .append(mContext.getString( - R.string.wifi_ca_cert_dialog_message_issuer_name_text, -- mPendingCaCertIssuerInfo.commonName)); -- if (!TextUtils.isEmpty(mPendingCaCertSubjectInfo.organization)) { -+ mPendingServerCertIssuerInfo.commonName)); -+ if (!TextUtils.isEmpty(mPendingServerCertSubjectInfo.organization)) { - contentBuilder.append(mContext.getString( - R.string.wifi_ca_cert_dialog_message_organization_text, -- mPendingCaCertSubjectInfo.organization)); -+ mPendingServerCertSubjectInfo.organization)); - } -- if (!TextUtils.isEmpty(mPendingCaCertSubjectInfo.email)) { -+ final Date expiration = mPendingServerCert.getNotAfter(); -+ if (expiration != null) { - contentBuilder.append(mContext.getString( -- R.string.wifi_ca_cert_dialog_message_contact_text, -- mPendingCaCertSubjectInfo.email)); -+ R.string.wifi_ca_cert_dialog_message_expiration_text, -+ DateFormat.getMediumDateFormat(mContext).format(expiration))); -+ } -+ final String fingerprint = getDigest(mPendingServerCert, "SHA256"); -+ if (!TextUtils.isEmpty(fingerprint)) { -+ contentBuilder.append(mContext.getString( -+ R.string.wifi_ca_cert_dialog_message_signature_name_text, fingerprint)); - } -- contentBuilder -- .append(mContext.getString( -- R.string.wifi_ca_cert_dialog_message_signature_name_text, -- signature.substring(0, 16))); - message = contentBuilder.toString(); - } else { - String hint = mContext.getString( -- R.string.wifi_ca_cert_dialog_preT_message_hint, mCurConfig.SSID); -+ R.string.wifi_ca_cert_dialog_preT_message_hint, mCurrentTofuConfig.SSID); - String linkText = mContext.getString( - R.string.wifi_ca_cert_dialog_preT_message_link); - message = hint + " " + linkText; -@@ -427,23 +620,39 @@ public class InsecureEapNetworkHandler { - new WifiDialogManager.SimpleDialogCallback() { - @Override - public void onPositiveButtonClicked() { -- handleAccept(mCurConfig.SSID); -+ if (mCurrentTofuConfig == null) { -+ return; -+ } -+ Log.d(TAG, "User accepted the server certificate"); -+ handleAccept(mCurrentTofuConfig.SSID); - } - - @Override - public void onNegativeButtonClicked() { -- handleReject(mCurConfig.SSID); -+ if (mCurrentTofuConfig == null) { -+ return; -+ } -+ Log.d(TAG, "User rejected the server certificate"); -+ handleReject(mCurrentTofuConfig.SSID); - } - - @Override - public void onNeutralButtonClicked() { - // Not used. -- handleReject(mCurConfig.SSID); -+ if (mCurrentTofuConfig == null) { -+ return; -+ } -+ Log.d(TAG, "User input neutral"); -+ handleReject(mCurrentTofuConfig.SSID); - } - - @Override - public void onCancelled() { -- handleReject(mCurConfig.SSID); -+ if (mCurrentTofuConfig == null) { -+ return; -+ } -+ Log.d(TAG, "User input canceled"); -+ handleReject(mCurrentTofuConfig.SSID); - } - }, - new WifiThreadRunner(mHandler)); -@@ -460,16 +669,16 @@ public class InsecureEapNetworkHandler { - } - - private void notifyUserForCaCertificate() { -- if (mCurConfig == null) return; -+ if (mCurrentTofuConfig == null) return; - if (useTrustOnFirstUse()) { -- if (null == mPendingCaCert) return; -+ if (null == mPendingRootCaCert) return; - if (null == mPendingServerCert) return; - } - dismissDialogAndNotification(); - - PendingIntent tapPendingIntent; - if (useTrustOnFirstUse()) { -- tapPendingIntent = genCaCertNotifIntent(ACTION_CERT_NOTIF_TAP, mCurConfig.SSID); -+ tapPendingIntent = genCaCertNotifIntent(ACTION_CERT_NOTIF_TAP, mCurrentTofuConfig.SSID); - } else { - Intent openLinkIntent = new Intent(Intent.ACTION_VIEW) - .setData(Uri.parse(mCaCertHelpLink)) -@@ -482,9 +691,10 @@ public class InsecureEapNetworkHandler { - ? mContext.getString(R.string.wifi_ca_cert_notification_title) - : mContext.getString(R.string.wifi_ca_cert_notification_preT_title); - String content = useTrustOnFirstUse() -- ? mContext.getString(R.string.wifi_ca_cert_notification_message, mCurConfig.SSID) -+ ? mContext.getString(R.string.wifi_ca_cert_notification_message, -+ mCurrentTofuConfig.SSID) - : mContext.getString(R.string.wifi_ca_cert_notification_preT_message, -- mCurConfig.SSID); -+ mCurrentTofuConfig.SSID); - Notification.Builder builder = mFacade.makeNotificationBuilder(mContext, - WifiService.NOTIFICATION_NETWORK_ALERTS) - .setSmallIcon(Icon.createWithResource(mContext.getWifiOverlayApkPkgName(), -@@ -502,11 +712,13 @@ public class InsecureEapNetworkHandler { - Notification.Action acceptAction = new Notification.Action.Builder( - null /* icon */, - mContext.getString(R.string.wifi_ca_cert_dialog_preT_continue_text), -- genCaCertNotifIntent(ACTION_CERT_NOTIF_ACCEPT, mCurConfig.SSID)).build(); -+ genCaCertNotifIntent(ACTION_CERT_NOTIF_ACCEPT, mCurrentTofuConfig.SSID)) -+ .build(); - Notification.Action rejectAction = new Notification.Action.Builder( - null /* icon */, - mContext.getString(R.string.wifi_ca_cert_dialog_preT_abort_text), -- genCaCertNotifIntent(ACTION_CERT_NOTIF_REJECT, mCurConfig.SSID)).build(); -+ genCaCertNotifIntent(ACTION_CERT_NOTIF_REJECT, mCurrentTofuConfig.SSID)) -+ .build(); - builder.addAction(rejectAction).addAction(acceptAction); - } - mNotificationManager.notify(SystemMessage.NOTE_SERVER_CA_CERTIFICATE, builder.build()); -@@ -521,18 +733,18 @@ public class InsecureEapNetworkHandler { - } - - private void clearInternalData() { -- mPendingCaCertDepth = -1; -- mPendingCaCert = null; -+ mPendingRootCaCertDepth = -1; -+ mPendingRootCaCert = null; - mPendingServerCert = null; -- mPendingCaCertSubjectInfo = null; -- mPendingCaCertIssuerInfo = null; -- mCurConfig = null; -+ mPendingServerCertSubjectInfo = null; -+ mPendingServerCertIssuerInfo = null; -+ mCurrentTofuConfig = null; - } - - private void clearNativeData() { - // PMK should be cleared or it would skip EAP flow next time. -- if (null != mCurConfig) { -- mWifiNative.removeNetworkCachedData(mCurConfig.networkId); -+ if (null != mCurrentTofuConfig) { -+ mWifiNative.removeNetworkCachedData(mCurrentTofuConfig.networkId); - } - // remove network so that supplicant's PMKSA cache is cleared - mWifiNative.removeAllNetworks(mInterfaceName); -@@ -551,19 +763,47 @@ public class InsecureEapNetworkHandler { - // If condition #2 occurs, clear existing data and notify the client mode - // via onError callback. - private boolean isConnectionValid(@Nullable String ssid) { -- if (TextUtils.isEmpty(ssid) || null == mCurConfig) { -+ if (TextUtils.isEmpty(ssid) || null == mCurrentTofuConfig) { - handleError(null); - return false; - } - -- if (!TextUtils.equals(ssid, mCurConfig.SSID)) { -- Log.w(TAG, "Target SSID " + mCurConfig.SSID -+ if (!TextUtils.equals(ssid, mCurrentTofuConfig.SSID)) { -+ Log.w(TAG, "Target SSID " + mCurrentTofuConfig.SSID - + " is different from TOFU returned SSID" + ssid); - return false; - } - return true; - } - -+ @VisibleForTesting -+ static String getDigest(X509Certificate x509Certificate, String algorithm) { -+ if (x509Certificate == null) { -+ return ""; -+ } -+ try { -+ byte[] bytes = x509Certificate.getEncoded(); -+ MessageDigest md = MessageDigest.getInstance(algorithm); -+ byte[] digest = md.digest(bytes); -+ return fingerprint(digest); -+ } catch (CertificateEncodingException ignored) { -+ return ""; -+ } catch (NoSuchAlgorithmException ignored) { -+ return ""; -+ } -+ } -+ -+ private static String fingerprint(byte[] bytes) { -+ if (bytes == null) { -+ return ""; -+ } -+ StringJoiner sj = new StringJoiner(":"); -+ for (byte b : bytes) { -+ sj.add(HexDump.toHexString(b)); -+ } -+ return sj.toString(); -+ } -+ - /** The callbacks object to notify the consumer. */ - public static class InsecureEapNetworkHandlerCallbacks { - /** -@@ -576,8 +816,9 @@ public class InsecureEapNetworkHandler { - * When a certificate is rejected, this callback is called. - * - * @param ssid SSID of the network. -+ * @param disconnectRequired Set to true if the network is currently connected - */ -- public void onReject(@NonNull String ssid) {} -+ public void onReject(@NonNull String ssid, boolean disconnectRequired) {} - /** - * When there are no valid data to handle this insecure EAP network, - * this callback is called. -diff --git a/service/java/com/android/server/wifi/WifiConfigManager.java b/service/java/com/android/server/wifi/WifiConfigManager.java -index d3e20bef27..ee6fea7ac8 100644 ---- a/service/java/com/android/server/wifi/WifiConfigManager.java -+++ b/service/java/com/android/server/wifi/WifiConfigManager.java -@@ -16,6 +16,7 @@ - - package com.android.server.wifi; - -+import static android.content.pm.PackageManager.PERMISSION_GRANTED; - import static android.net.wifi.WifiManager.WIFI_FEATURE_TRUST_ON_FIRST_USE; - - import android.Manifest; -@@ -1433,6 +1434,30 @@ public class WifiConfigManager { - existingInternalConfig); - } - -+ if (config.isEnterprise() -+ && config.enterpriseConfig.isEapMethodServerCertUsed() -+ && !config.enterpriseConfig.isMandatoryParameterSetForServerCertValidation() -+ && !config.enterpriseConfig.isTrustOnFirstUseEnabled()) { -+ boolean isSettingsOrSuw = mContext.checkPermission(Manifest.permission.NETWORK_SETTINGS, -+ -1 /* pid */, uid) == PERMISSION_GRANTED -+ || mContext.checkPermission(Manifest.permission.NETWORK_SETUP_WIZARD, -+ -1 /* pid */, uid) == PERMISSION_GRANTED; -+ if (!(mWifiInjector.getWifiGlobals().isInsecureEnterpriseConfigurationAllowed() -+ && isSettingsOrSuw)) { -+ Log.e(TAG, "Enterprise network configuration is missing either a Root CA " -+ + "or a domain name"); -+ return new Pair<>( -+ new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID), -+ existingInternalConfig); -+ } -+ Log.w(TAG, "Insecure Enterprise network " + config.SSID -+ + " configured by Settings/SUW"); -+ -+ // Implicit user approval, when creating an insecure connection which is allowed -+ // in the configuration of the device -+ newInternalConfig.enterpriseConfig.setUserApproveNoCaCert(true); -+ } -+ - // Update the keys for saved enterprise networks. For Passpoint, the certificates - // and keys are installed at the time the provider is installed. For suggestion enterprise - // network the certificates and keys are installed at the time the suggestion is added -@@ -1484,11 +1509,6 @@ public class WifiConfigManager { - newInternalConfig.getNetworkSelectionStatus().setHasEverConnected(false); - } - -- // Ensure that the user approve flag is set to false for a new network. -- if (newNetwork && config.isEnterprise()) { -- config.enterpriseConfig.setUserApproveNoCaCert(false); -- } -- - // Add it to our internal map. This will replace any existing network configuration for - // updates. - try { -@@ -4130,7 +4150,7 @@ public class WifiConfigManager { - try { - if (newConfig.enterpriseConfig.isTrustOnFirstUseEnabled()) { - newConfig.enterpriseConfig.setCaCertificateForTrustOnFirstUse(caCert); -- // setCaCertificate will mark that this CA certifiate should be removed on -+ // setCaCertificate will mark that this CA certificate should be removed on - // removing this configuration. - newConfig.enterpriseConfig.enableTrustOnFirstUse(false); - } else { -diff --git a/service/java/com/android/server/wifi/WifiKeyStore.java b/service/java/com/android/server/wifi/WifiKeyStore.java -index a69614090c..deb2e9d94c 100644 ---- a/service/java/com/android/server/wifi/WifiKeyStore.java -+++ b/service/java/com/android/server/wifi/WifiKeyStore.java -@@ -315,38 +315,41 @@ public class WifiKeyStore { - if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE_192_BIT)) { - // Read the CA certificates, and initialize - String[] caAliases = config.enterpriseConfig.getCaCertificateAliases(); -- -- if (caAliases == null || caAliases.length == 0) { -- Log.e(TAG, "No CA aliases in profile"); -- return false; -- } -- - int caCertType = -1; -- int prevCaCertType = -1; -- for (String caAlias : caAliases) { -- Certificate caCert = null; -- try { -- caCert = mKeyStore.getCertificate(caAlias); -- } catch (KeyStoreException e) { -- Log.e(TAG, "Failed to get Suite-B certificate", e); -- } -- if (caCert == null || !(caCert instanceof X509Certificate)) { -- Log.e(TAG, "Failed reading CA certificate for Suite-B"); -- return false; -- } - -- // Confirm that the CA certificate is compatible with Suite-B requirements -- caCertType = getSuiteBCipherFromCert((X509Certificate) caCert); -- if (caCertType < 0) { -+ // In TOFU mode, configure the security mode based on the user certificate only. -+ if (!config.enterpriseConfig.isTrustOnFirstUseEnabled()) { -+ if (caAliases == null || caAliases.length == 0) { -+ Log.e(TAG, "No CA aliases in profile"); - return false; - } -- if (prevCaCertType != -1) { -- if (prevCaCertType != caCertType) { -- Log.e(TAG, "Incompatible CA certificates"); -+ -+ int prevCaCertType = -1; -+ for (String caAlias : caAliases) { -+ Certificate caCert = null; -+ try { -+ caCert = mKeyStore.getCertificate(caAlias); -+ } catch (KeyStoreException e) { -+ Log.e(TAG, "Failed to get Suite-B certificate", e); -+ } -+ if (caCert == null || !(caCert instanceof X509Certificate)) { -+ Log.e(TAG, "Failed reading CA certificate for Suite-B"); - return false; - } -+ -+ // Confirm that the CA certificate is compatible with Suite-B requirements -+ caCertType = getSuiteBCipherFromCert((X509Certificate) caCert); -+ if (caCertType < 0) { -+ return false; -+ } -+ if (prevCaCertType != -1) { -+ if (prevCaCertType != caCertType) { -+ Log.e(TAG, "Incompatible CA certificates"); -+ return false; -+ } -+ } -+ prevCaCertType = caCertType; - } -- prevCaCertType = caCertType; - } - - Certificate clientCert = null; -@@ -366,7 +369,8 @@ public class WifiKeyStore { - return false; - } - -- if (clientCertType == caCertType) { -+ if (clientCertType == caCertType -+ || config.enterpriseConfig.isTrustOnFirstUseEnabled()) { - config.enableSuiteBCiphers( - clientCertType == WifiConfiguration.SuiteBCipher.ECDHE_ECDSA, - clientCertType == WifiConfiguration.SuiteBCipher.ECDHE_RSA); -diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java -index 40acc85cc5..b044508eb7 100644 ---- a/service/java/com/android/server/wifi/WifiServiceImpl.java -+++ b/service/java/com/android/server/wifi/WifiServiceImpl.java -@@ -528,9 +528,6 @@ public class WifiServiceImpl extends BaseWifiService { - if (!mWifiConfigManager.loadFromStore()) { - Log.e(TAG, "Failed to load from config store"); - } -- if (!mWifiGlobals.isInsecureEnterpriseConfigurationAllowed()) { -- mWifiConfigManager.updateTrustOnFirstUseFlag(isTrustOnFirstUseSupported()); -- } - mWifiConfigManager.incrementNumRebootsSinceLastUse(); - // config store is read, check if verbose logging is enabled. - enableVerboseLoggingInternal( -@@ -795,6 +792,10 @@ public class WifiServiceImpl extends BaseWifiService { - mLohsSoftApTracker.handleBootCompleted(); - mWifiInjector.getSarManager().handleBootCompleted(); - mIsBootComplete = true; -+ // HW capabilities is ready after boot completion. -+ if (!mWifiGlobals.isInsecureEnterpriseConfigurationAllowed()) { -+ mWifiConfigManager.updateTrustOnFirstUseFlag(isTrustOnFirstUseSupported()); -+ } - }); - } - -diff --git a/service/proto/src/metrics.proto b/service/proto/src/metrics.proto -index 09a596f984..871eb2c750 100644 ---- a/service/proto/src/metrics.proto -+++ b/service/proto/src/metrics.proto -@@ -1047,6 +1047,9 @@ message ConnectionEvent { - - // The reason code if a user rejects this connection. - AUTH_FAILURE_REJECTED_BY_USER = 7; -+ -+ // The reason code if an insecure Enterprise connection requires user's approval -+ DISCONNECTED_USER_APPROVAL_NEEDED = 8; - } - - // Entity that recommended connecting to this network. -diff --git a/service/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java b/service/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java -index ca8f5b031c..cef996fef1 100644 ---- a/service/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java -+++ b/service/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java -@@ -7853,18 +7853,18 @@ public class ClientModeImplTest extends WifiBaseTest { - WifiManager.WIFI_FEATURE_TRUST_ON_FIRST_USE); - } - mCmi.mInsecureEapNetworkHandler = mInsecureEapNetworkHandler; -- when(mInsecureEapNetworkHandler.startUserApprovalIfNecessary(anyBoolean())) -- .thenReturn(true); - - WifiConfiguration eapTlsConfig = spy(WifiConfigurationTestUtil.createEapNetwork( - WifiEnterpriseConfig.Eap.TLS, WifiEnterpriseConfig.Phase2.NONE)); - eapTlsConfig.networkId = FRAMEWORK_NETWORK_ID; - eapTlsConfig.SSID = TEST_SSID; -- if (isAtLeastT) { -+ if (isAtLeastT && isTrustOnFirstUseSupported) { - eapTlsConfig.enterpriseConfig.enableTrustOnFirstUse(true); - } - eapTlsConfig.enterpriseConfig.setCaPath(""); - eapTlsConfig.enterpriseConfig.setDomainSuffixMatch(""); -+ eapTlsConfig.setRandomizedMacAddress(TEST_LOCAL_MAC_ADDRESS); -+ eapTlsConfig.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_AUTO; - - initializeAndAddNetworkAndVerifySuccess(eapTlsConfig); - -@@ -7879,31 +7879,32 @@ public class ClientModeImplTest extends WifiBaseTest { - } - verify(mInsecureEapNetworkHandler).prepareConnection(eq(eapTlsConfig)); - -- mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, -- new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR, -- SupplicantState.ASSOCIATED)); -- mLooper.dispatchAll(); -- - if (isTrustOnFirstUseSupported) { -+ mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, -+ new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR, -+ SupplicantState.ASSOCIATED)); -+ mLooper.dispatchAll(); -+ - mCmi.sendMessage(WifiMonitor.TOFU_ROOT_CA_CERTIFICATE, - FRAMEWORK_NETWORK_ID, 0, FakeKeys.CA_CERT0); - mLooper.dispatchAll(); -- verify(mInsecureEapNetworkHandler).setPendingCertificate( -+ verify(mInsecureEapNetworkHandler).addPendingCertificate( - eq(eapTlsConfig.SSID), eq(0), eq(FakeKeys.CA_CERT0)); -- } - -- mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, -- new NetworkConnectionEventInfo(0, TEST_WIFI_SSID, TEST_BSSID_STR, false)); -- mLooper.dispatchAll(); -- -- mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, -- new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR, -- SupplicantState.COMPLETED)); -- mLooper.dispatchAll(); -+ // Adding a certificate in depth 0 will cause a disconnection when TOFU is supported -+ DisconnectEventInfo disconnectEventInfo = -+ new DisconnectEventInfo(TEST_SSID, TEST_BSSID_STR, 3, true); -+ mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, disconnectEventInfo); -+ mCmi.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, -+ new StateChangeResult(0, TEST_WIFI_SSID, TEST_BSSID_STR, -+ SupplicantState.DISCONNECTED)); -+ mLooper.dispatchAll(); -+ } - - verify(mInsecureEapNetworkHandler).startUserApprovalIfNecessary(eq(isUserSelected)); -- assertEquals("L3ProvisioningState", getCurrentState().getName()); -- -+ if (isTrustOnFirstUseSupported) { -+ assertEquals("DisconnectedState", getCurrentState().getName()); -+ } - return eapTlsConfig; - } - -@@ -7919,9 +7920,7 @@ public class ClientModeImplTest extends WifiBaseTest { - - mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onAccept(testConfig.SSID); - mLooper.dispatchAll(); -- injectDhcpSuccess(); -- mLooper.dispatchAll(); -- assertEquals("L3ConnectedState", getCurrentState().getName()); -+ verify(mWifiConnectivityManager).forceConnectivityScan(eq(ClientModeImpl.WIFI_WORK_SOURCE)); - } - - /** -@@ -7934,9 +7933,15 @@ public class ClientModeImplTest extends WifiBaseTest { - assumeTrue(SdkLevel.isAtLeastT()); - WifiConfiguration testConfig = setupTrustOnFirstUse(true, true, true); - -- mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onReject(testConfig.SSID); -+ mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onReject(testConfig.SSID, false); - mLooper.dispatchAll(); -- verify(mWifiNative).disconnect(eq(WIFI_IFACE_NAME)); -+ verify(mWifiConnectivityManager, never()) -+ .forceConnectivityScan(eq(ClientModeImpl.WIFI_WORK_SOURCE)); -+ verify(mWifiMetrics).endConnectionEvent( -+ any(), eq(WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION), -+ eq(WifiMetricsProto.ConnectionEvent.HLF_NONE), -+ eq(WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN), -+ anyInt()); - } - - /** -@@ -7951,7 +7956,13 @@ public class ClientModeImplTest extends WifiBaseTest { - - mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onError(testConfig.SSID); - mLooper.dispatchAll(); -- verify(mWifiNative).disconnect(eq(WIFI_IFACE_NAME)); -+ verify(mWifiConnectivityManager, never()) -+ .forceConnectivityScan(eq(ClientModeImpl.WIFI_WORK_SOURCE)); -+ verify(mWifiMetrics).endConnectionEvent( -+ any(), eq(WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION), -+ eq(WifiMetricsProto.ConnectionEvent.HLF_NONE), -+ eq(WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN), -+ anyInt()); - } - - /** -@@ -7967,9 +7978,7 @@ public class ClientModeImplTest extends WifiBaseTest { - - mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onAccept(testConfig.SSID); - mLooper.dispatchAll(); -- injectDhcpSuccess(); -- mLooper.dispatchAll(); -- assertEquals("L3ConnectedState", getCurrentState().getName()); -+ verify(mWifiConnectivityManager).forceConnectivityScan(eq(ClientModeImpl.WIFI_WORK_SOURCE)); - } - - /** -@@ -7983,9 +7992,15 @@ public class ClientModeImplTest extends WifiBaseTest { - assumeTrue(SdkLevel.isAtLeastT()); - WifiConfiguration testConfig = setupTrustOnFirstUse(true, true, false); - -- mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onReject(testConfig.SSID); -+ mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onReject(testConfig.SSID, false); - mLooper.dispatchAll(); -- verify(mWifiNative).disconnect(eq(WIFI_IFACE_NAME)); -+ verify(mWifiConnectivityManager, never()) -+ .forceConnectivityScan(eq(ClientModeImpl.WIFI_WORK_SOURCE)); -+ verify(mWifiMetrics).endConnectionEvent( -+ any(), eq(WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION), -+ eq(WifiMetricsProto.ConnectionEvent.HLF_NONE), -+ eq(WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN), -+ anyInt()); - } - - /** -@@ -8000,7 +8015,13 @@ public class ClientModeImplTest extends WifiBaseTest { - - mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onError(testConfig.SSID); - mLooper.dispatchAll(); -- verify(mWifiNative).disconnect(eq(WIFI_IFACE_NAME)); -+ verify(mWifiConnectivityManager, never()) -+ .forceConnectivityScan(eq(ClientModeImpl.WIFI_WORK_SOURCE)); -+ verify(mWifiMetrics).endConnectionEvent( -+ any(), eq(WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION), -+ eq(WifiMetricsProto.ConnectionEvent.HLF_NONE), -+ eq(WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN), -+ anyInt()); - } - - /** -@@ -8015,9 +8036,8 @@ public class ClientModeImplTest extends WifiBaseTest { - - mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onAccept(testConfig.SSID); - mLooper.dispatchAll(); -- injectDhcpSuccess(); -- mLooper.dispatchAll(); -- assertEquals("L3ConnectedState", getCurrentState().getName()); -+ verify(mWifiMetrics, never()).endConnectionEvent( -+ any(), anyInt(), anyInt(), anyInt(), anyInt()); - } - - /** -@@ -8030,23 +8050,10 @@ public class ClientModeImplTest extends WifiBaseTest { - assumeFalse(SdkLevel.isAtLeastT()); - WifiConfiguration testConfig = setupLegacyEapNetworkTest(true); - -- mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onReject(testConfig.SSID); -- mLooper.dispatchAll(); -- verify(mWifiNative).disconnect(eq(WIFI_IFACE_NAME)); -- } -- -- /** -- * Verify legacy EAP network handling. -- * - This network is automatically connected. -- * - Errors occur in InsecureEapNetworkHandler. -- */ -- @Test -- public void verifyLegacyEapNetworkErrorWhenConnectByUser() throws Exception { -- assumeFalse(SdkLevel.isAtLeastT()); -- WifiConfiguration testConfig = setupLegacyEapNetworkTest(true); -- -- mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onError(testConfig.SSID); -+ mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onReject(testConfig.SSID, true); - mLooper.dispatchAll(); -+ verify(mWifiConnectivityManager, never()) -+ .forceConnectivityScan(eq(ClientModeImpl.WIFI_WORK_SOURCE)); - verify(mWifiNative).disconnect(eq(WIFI_IFACE_NAME)); - } - -@@ -8061,40 +8068,26 @@ public class ClientModeImplTest extends WifiBaseTest { - WifiConfiguration testConfig = setupLegacyEapNetworkTest(false); - - mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onAccept(testConfig.SSID); -- injectDhcpSuccess(); - mLooper.dispatchAll(); -- assertEquals("L3ConnectedState", getCurrentState().getName()); -+ verify(mWifiMetrics, never()).endConnectionEvent( -+ any(), anyInt(), anyInt(), anyInt(), anyInt()); - } - - /** - * Verify legacy EAP network handling. - * - This network is automatically connected. -- * - Tap "Don't connect" on the notification -+ * - Tap "Disconnect now" on the notification - */ - @Test - public void verifyLegacyEapNetworkRejectOnNotificationWhenAutoConnect() throws Exception { - assumeFalse(SdkLevel.isAtLeastT()); - WifiConfiguration testConfig = setupLegacyEapNetworkTest(false); - -- mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onReject(testConfig.SSID); -+ mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onReject(testConfig.SSID, true); - mLooper.dispatchAll(); -- - verify(mFrameworkFacade, never()).makeAlertDialogBuilder(any()); -- verify(mWifiNative).disconnect(eq(WIFI_IFACE_NAME)); -- } -- -- /** -- * Verify legacy EAP network handling. -- * - This network is automatically connected. -- * - Errors occur in InsecureEapNetworkHandler. -- */ -- @Test -- public void verifyLegacyEapNetworkErrorWhenAutoConnect() throws Exception { -- assumeFalse(SdkLevel.isAtLeastT()); -- WifiConfiguration testConfig = setupLegacyEapNetworkTest(false); -- -- mCmi.mInsecureEapNetworkHandlerCallbacksImpl.onError(testConfig.SSID); -- mLooper.dispatchAll(); -+ verify(mWifiConnectivityManager, never()) -+ .forceConnectivityScan(eq(ClientModeImpl.WIFI_WORK_SOURCE)); - verify(mWifiNative).disconnect(eq(WIFI_IFACE_NAME)); - } - -diff --git a/service/tests/wifitests/src/com/android/server/wifi/InsecureEapNetworkHandlerTest.java b/service/tests/wifitests/src/com/android/server/wifi/InsecureEapNetworkHandlerTest.java -index aed3753ffc..b83f6e7e6c 100644 ---- a/service/tests/wifitests/src/com/android/server/wifi/InsecureEapNetworkHandlerTest.java -+++ b/service/tests/wifitests/src/com/android/server/wifi/InsecureEapNetworkHandlerTest.java -@@ -16,15 +16,20 @@ - - package com.android.server.wifi; - -+import static com.android.server.wifi.InsecureEapNetworkHandler.TOFU_ANONYMOUS_IDENTITY; -+ - import static org.junit.Assert.assertEquals; -+import static org.junit.Assert.assertNotNull; - import static org.junit.Assert.assertTrue; - import static org.junit.Assume.assumeFalse; - import static org.junit.Assume.assumeTrue; - import static org.mockito.Mockito.any; - import static org.mockito.Mockito.anyInt; -+import static org.mockito.Mockito.anyString; - import static org.mockito.Mockito.argThat; - import static org.mockito.Mockito.atLeastOnce; - import static org.mockito.Mockito.eq; -+import static org.mockito.Mockito.mock; - import static org.mockito.Mockito.never; - import static org.mockito.Mockito.spy; - import static org.mockito.Mockito.validateMockitoUsage; -@@ -37,11 +42,15 @@ import android.content.Intent; - import android.net.wifi.WifiConfiguration; - import android.net.wifi.WifiContext; - import android.net.wifi.WifiEnterpriseConfig; -+import android.net.wifi.util.HexEncoding; - import android.os.Handler; -+import android.text.TextUtils; - - import androidx.test.filters.SmallTest; - - import com.android.modules.utils.build.SdkLevel; -+import com.android.server.wifi.util.CertificateSubjectInfo; -+import com.android.wifi.resources.R; - - import org.junit.After; - import org.junit.Before; -@@ -51,9 +60,13 @@ import org.mockito.ArgumentCaptor; - import org.mockito.Captor; - import org.mockito.Mock; - import org.mockito.MockitoAnnotations; -+import org.mockito.stubbing.Answer; - -+import java.nio.charset.StandardCharsets; - import java.security.cert.X509Certificate; - -+import javax.security.auth.x500.X500Principal; -+ - /** - * Unit tests for {@link com.android.server.wifi.InsecureEapNetworkHandlerTest}. - */ -@@ -65,7 +78,11 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - private static final int ACTION_TAP = 2; - private static final String WIFI_IFACE_NAME = "wlan-test-9"; - private static final int FRAMEWORK_NETWORK_ID = 2; -- private static final String TEST_SSID = "test_ssid"; -+ private static final String TEST_SSID = "\"test_ssid\""; -+ private static final String TEST_IDENTITY = "userid"; -+ private static final String TEST_PASSWORD = "myPassWord!"; -+ private static final String TEST_EXPECTED_SHA_256_SIGNATURE = "78:A6:27:31:03:D1:7C:39:A0:B6:12" -+ + ":6E:22:6C:EC:70:E3:33:37:F4:BC:6A:38:06:74:01:B5:4A:33:E7:8E:AD"; - - @Mock WifiContext mContext; - @Mock WifiConfigManager mWifiConfigManager; -@@ -94,11 +111,34 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - when(mContext.getString(anyInt())).thenReturn("TestString"); - when(mContext.getString(anyInt(), any())).thenReturn("TestStringWithArgument"); - when(mContext.getText(anyInt())).thenReturn("TestStr"); -+ when(mContext.getString(eq(R.string.wifi_ca_cert_dialog_message_issuer_name_text), -+ anyString())) -+ .thenAnswer((Answer) invocation -> -+ "Issuer Name:\n" + invocation.getArguments()[1] + "\n\n"); -+ when(mContext.getString(eq(R.string.wifi_ca_cert_dialog_message_server_name_text), -+ anyString())) -+ .thenAnswer((Answer) invocation -> -+ "Server Name:\n" + invocation.getArguments()[1] + "\n\n"); -+ when(mContext.getString(eq(R.string.wifi_ca_cert_dialog_message_organization_text), -+ anyString())) -+ .thenAnswer((Answer) invocation -> -+ "Organization:\n" + invocation.getArguments()[1] + "\n\n"); -+ when(mContext.getString(eq(R.string.wifi_ca_cert_dialog_message_contact_text), -+ anyString())) -+ .thenAnswer((Answer) invocation -> -+ "Contact:\n" + invocation.getArguments()[1] + "\n\n"); -+ when(mContext.getString(eq(R.string.wifi_ca_cert_dialog_message_signature_name_text), -+ anyString())) -+ .thenAnswer((Answer) invocation -> -+ "SHA-256 Fingerprint:\n" + invocation.getArguments()[1] + "\n\n"); - when(mContext.getWifiOverlayApkPkgName()).thenReturn("test.com.android.wifi.resources"); - when(mContext.getResources()).thenReturn(mResources); - when(mWifiDialogManager.createSimpleDialogWithUrl( - any(), any(), any(), anyInt(), anyInt(), any(), any(), any(), any(), any())) - .thenReturn(mTofuAlertDialog); -+ when(mWifiDialogManager.createSimpleDialog( -+ any(), any(), any(), any(), any(), any(), any())) -+ .thenReturn(mTofuAlertDialog); - - when(mFrameworkFacade.makeNotificationBuilder(any(), any())) - .thenReturn(mNotificationBuilder); -@@ -205,8 +245,11 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - - WifiConfiguration config = prepareWifiConfiguration(isAtLeastT); - setupTest(config, isAtLeastT, isTrustOnFirstUseSupported); -- assertTrue(mInsecureEapNetworkHandler.startUserApprovalIfNecessary(isUserSelected)); -+ mInsecureEapNetworkHandler.startUserApprovalIfNecessary(isUserSelected); - verify(mCallbacks).onError(eq(config.SSID)); -+ verify(mWifiConfigManager, atLeastOnce()).updateNetworkSelectionStatus(eq(config.networkId), -+ eq(WifiConfiguration.NetworkSelectionStatus -+ .DISABLED_BY_WIFI_MANAGER)); - } - - /** -@@ -220,7 +263,7 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - - WifiConfiguration config = prepareWifiConfiguration(isAtLeastT); - setupTest(config, isAtLeastT, isTrustOnFirstUseSupported); -- assertTrue(mInsecureEapNetworkHandler.startUserApprovalIfNecessary(isUserSelected)); -+ mInsecureEapNetworkHandler.startUserApprovalIfNecessary(isUserSelected); - verify(mCallbacks, never()).onError(any()); - } - -@@ -311,9 +354,40 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - isTrustOnFirstUseSupported, isUserSelected, needUserApproval); - } - -+ private X509Certificate generateMockCert(String subject, String issuer, boolean isCa) { -+ X509Certificate mockCert = mock(X509Certificate.class); -+ X500Principal mockSubjectPrincipal = mock(X500Principal.class); -+ when(mockCert.getSubjectX500Principal()).thenReturn(mockSubjectPrincipal); -+ when(mockSubjectPrincipal.getName()).thenReturn("C=TW,ST=Taiwan,L=Taipei" -+ + ",O=" + subject + " Organization" -+ + ",CN=" + subject -+ + ",1.2.840.113549.1.9.1=#1614" + String.valueOf(HexEncoding.encode( -+ (subject + "@email.com").getBytes(StandardCharsets.UTF_8)))); -+ try { -+ when(mockCert.getEncoded()).thenReturn(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); -+ } catch (Exception e) { -+ // nothing -+ } -+ X500Principal mockIssuerX500Principal = mock(X500Principal.class); -+ when(mockCert.getIssuerX500Principal()).thenReturn(mockIssuerX500Principal); -+ when(mockIssuerX500Principal.getName()).thenReturn("C=TW,ST=Taiwan,L=Taipei" -+ + ",O=" + issuer + " Organization" -+ + ",CN=" + issuer -+ + ",1.2.840.113549.1.9.1=#1614" + String.valueOf(HexEncoding.encode( -+ (issuer + "@email.com").getBytes(StandardCharsets.UTF_8)))); -+ -+ when(mockCert.getSignature()).thenReturn(new byte[]{ -+ (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef, -+ (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78, -+ (byte) 0x90, (byte) 0xab, (byte) 0xcd, (byte) 0xef}); -+ -+ when(mockCert.getBasicConstraints()).thenReturn(isCa ? 99 : -1); -+ return mockCert; -+ } -+ - private WifiConfiguration prepareWifiConfiguration(boolean isAtLeastT) { - WifiConfiguration config = spy(WifiConfigurationTestUtil.createEapNetwork( -- WifiEnterpriseConfig.Eap.TLS, WifiEnterpriseConfig.Phase2.NONE)); -+ WifiEnterpriseConfig.Eap.TTLS, WifiEnterpriseConfig.Phase2.MSCHAPV2)); - config.networkId = FRAMEWORK_NETWORK_ID; - config.SSID = TEST_SSID; - if (isAtLeastT) { -@@ -321,6 +395,8 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - } - config.enterpriseConfig.setCaPath(""); - config.enterpriseConfig.setDomainSuffixMatch(""); -+ config.enterpriseConfig.setIdentity(TEST_IDENTITY); -+ config.enterpriseConfig.setPassword(TEST_PASSWORD); - return config; - } - -@@ -338,14 +414,34 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - mWifiNative, - mFrameworkFacade, - mWifiNotificationManager, -- mWifiDialogManager, isTrustOnFirstUseSupported, -+ mWifiDialogManager, -+ isTrustOnFirstUseSupported, - isInsecureEnterpriseConfigurationAllowed, - mCallbacks, - WIFI_IFACE_NAME, - mHandler); - -+ if (isTrustOnFirstUseSupported -+ && (config.enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.TTLS -+ || config.enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.PEAP) -+ && config.enterpriseConfig.getPhase2Method() != WifiEnterpriseConfig.Phase2.NONE) { -+ // Verify that the configuration contains an identity -+ assertEquals(TEST_IDENTITY, config.enterpriseConfig.getIdentity()); -+ assertTrue(TextUtils.isEmpty(config.enterpriseConfig.getAnonymousIdentity())); -+ assertEquals(TEST_PASSWORD, config.enterpriseConfig.getPassword()); -+ } - mInsecureEapNetworkHandler.prepareConnection(config); - -+ if (isTrustOnFirstUseSupported && config.enterpriseConfig.isTrustOnFirstUseEnabled() -+ && (config.enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.TTLS -+ || config.enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.PEAP) -+ && config.enterpriseConfig.getPhase2Method() != WifiEnterpriseConfig.Phase2.NONE) { -+ // Verify identities are cleared -+ assertTrue(TextUtils.isEmpty(config.enterpriseConfig.getIdentity())); -+ assertEquals(TOFU_ANONYMOUS_IDENTITY, config.enterpriseConfig.getAnonymousIdentity()); -+ assertTrue(TextUtils.isEmpty(config.enterpriseConfig.getPassword())); -+ } -+ - if (isTrustOnFirstUseSupported && config.enterpriseConfig.isTrustOnFirstUseEnabled()) { - verify(mContext, atLeastOnce()).registerReceiver( - mBroadcastReceiverCaptor.capture(), -@@ -379,34 +475,13 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - WifiConfiguration config = prepareWifiConfiguration(isAtLeastT); - setupTest(config, isAtLeastT, isTrustOnFirstUseSupported); - -- mInsecureEapNetworkHandler.setPendingCertificate(config.SSID, 1, FakeKeys.CA_CERT0); -- mInsecureEapNetworkHandler.setPendingCertificate(config.SSID, 0, FakeKeys.CLIENT_CERT); -- -- verifyTrustOnFirstUseFlow(config, ACTION_ACCEPT, isTrustOnFirstUseSupported, -- isUserSelected, needUserApproval, FakeKeys.CA_CERT0, FakeKeys.CLIENT_CERT); -- } -- -- /** -- * Verify Trust On First Use flow with a reversal cert chain -- * - This network is selected by a user. -- * - Accept the connection. -- */ -- @Test -- public void verifyTrustOnFirstUseAcceptWhenConnectByUserWithReversalOrderChain() -- throws Exception { -- assumeTrue(SdkLevel.isAtLeastT()); -- boolean isAtLeastT = true, isTrustOnFirstUseSupported = true, isUserSelected = true; -- boolean needUserApproval = true; -- -- WifiConfiguration config = prepareWifiConfiguration(isAtLeastT); -- setupTest(config, isAtLeastT, isTrustOnFirstUseSupported); -- -- mInsecureEapNetworkHandler.setPendingCertificate(config.SSID, 0, FakeKeys.CLIENT_CERT); -- mInsecureEapNetworkHandler.setPendingCertificate(config.SSID, 1, FakeKeys.CA_CERT1); -- mInsecureEapNetworkHandler.setPendingCertificate(config.SSID, 2, FakeKeys.CA_CERT0); -+ X509Certificate mockCaCert = generateMockCert("ca", "ca", true); -+ X509Certificate mockServerCert = generateMockCert("server", "ca", false); -+ mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 1, mockCaCert); -+ mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 0, mockServerCert); - - verifyTrustOnFirstUseFlow(config, ACTION_ACCEPT, isTrustOnFirstUseSupported, -- isUserSelected, needUserApproval, FakeKeys.CA_CERT0, FakeKeys.CLIENT_CERT); -+ isUserSelected, needUserApproval, mockCaCert, mockServerCert); - } - - /** -@@ -424,10 +499,11 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - WifiConfiguration config = prepareWifiConfiguration(isAtLeastT); - setupTest(config, isAtLeastT, isTrustOnFirstUseSupported); - -- mInsecureEapNetworkHandler.setPendingCertificate(config.SSID, 0, FakeKeys.CA_CERT0); -+ X509Certificate mockSelfSignedCert = generateMockCert("self", "self", false); -+ mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 0, mockSelfSignedCert); - - verifyTrustOnFirstUseFlow(config, ACTION_ACCEPT, isTrustOnFirstUseSupported, -- isUserSelected, needUserApproval, FakeKeys.CA_CERT0, FakeKeys.CA_CERT0); -+ isUserSelected, needUserApproval, mockSelfSignedCert, mockSelfSignedCert); - } - - /** -@@ -440,18 +516,19 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - public void verifyOnErrorWithoutCert() throws Exception { - assumeTrue(SdkLevel.isAtLeastT()); - boolean isAtLeastT = true, isTrustOnFirstUseSupported = true, isUserSelected = true; -- boolean needUserApproval = true; - - WifiConfiguration config = prepareWifiConfiguration(isAtLeastT); - setupTest(config, isAtLeastT, isTrustOnFirstUseSupported); - -- assertEquals(needUserApproval, -- mInsecureEapNetworkHandler.startUserApprovalIfNecessary(isUserSelected)); -+ mInsecureEapNetworkHandler.startUserApprovalIfNecessary(isUserSelected); - verify(mCallbacks).onError(eq(config.SSID)); -+ verify(mWifiConfigManager, atLeastOnce()).updateNetworkSelectionStatus(eq(config.networkId), -+ eq(WifiConfiguration.NetworkSelectionStatus -+ .DISABLED_BY_WIFI_MANAGER)); - } - - /** -- * Verify that the connection should be terminated. -+ * Verify that the connection should be upgraded to TOFU. - * - TOFU is supported. - * - Insecure EAP network is not allowed. - * - TOFU is not enabled -@@ -461,17 +538,18 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - throws Exception { - assumeTrue(SdkLevel.isAtLeastT()); - boolean isAtLeastT = true, isTrustOnFirstUseSupported = true, isUserSelected = true; -- boolean needUserApproval = true; - - WifiConfiguration config = prepareWifiConfiguration(isAtLeastT); - config.enterpriseConfig.enableTrustOnFirstUse(false); - setupTest(config, isAtLeastT, isTrustOnFirstUseSupported); - -- mInsecureEapNetworkHandler.setPendingCertificate(config.SSID, 0, FakeKeys.CA_CERT0); -+ mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 1, -+ generateMockCert("ca", "ca", true)); -+ mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 0, -+ generateMockCert("server", "ca", false)); - -- assertEquals(needUserApproval, -- mInsecureEapNetworkHandler.startUserApprovalIfNecessary(isUserSelected)); -- verify(mCallbacks).onError(eq(config.SSID)); -+ mInsecureEapNetworkHandler.startUserApprovalIfNecessary(isUserSelected); -+ assertTrue(config.enterpriseConfig.isTrustOnFirstUseEnabled()); - } - - /** -@@ -479,51 +557,179 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - * - TOFU is supported. - * - Insecure EAP network is allowed. - * - TOFU is not enabled -+ * - No user approval is needed. - */ - @Test - public void verifyNoErrorWithTofuDisabledWhenInsecureEapNetworkIsAllowed() - throws Exception { - assumeTrue(SdkLevel.isAtLeastT()); - boolean isAtLeastT = true, isTrustOnFirstUseSupported = true, isUserSelected = true; -- boolean needUserApproval = true, isInsecureEnterpriseConfigurationAllowed = true; -+ boolean isInsecureEnterpriseConfigurationAllowed = true; - - WifiConfiguration config = prepareWifiConfiguration(isAtLeastT); - config.enterpriseConfig.enableTrustOnFirstUse(false); - setupTest(config, isAtLeastT, isTrustOnFirstUseSupported, - isInsecureEnterpriseConfigurationAllowed); - -- mInsecureEapNetworkHandler.setPendingCertificate(config.SSID, 0, FakeKeys.CA_CERT0); -+ mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 1, -+ generateMockCert("ca", "ca", true)); -+ mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 0, -+ generateMockCert("server", "ca", false)); - -- assertEquals(needUserApproval, -- mInsecureEapNetworkHandler.startUserApprovalIfNecessary(isUserSelected)); -+ mInsecureEapNetworkHandler.startUserApprovalIfNecessary(isUserSelected); -+ verify(mCallbacks, never()).onError(any()); -+ } -+ -+ /** -+ * Verify that it reports errors if the cert chain is headless. -+ */ -+ @Test -+ public void verifyOnErrorWithHeadlessCertChain() throws Exception { -+ assumeTrue(SdkLevel.isAtLeastT()); -+ boolean isAtLeastT = true, isTrustOnFirstUseSupported = true, isUserSelected = true; -+ -+ WifiConfiguration config = prepareWifiConfiguration(isAtLeastT); -+ setupTest(config, isAtLeastT, isTrustOnFirstUseSupported); -+ -+ // Missing root CA cert. -+ mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 0, -+ generateMockCert("server", "ca", false)); -+ -+ mInsecureEapNetworkHandler.startUserApprovalIfNecessary(isUserSelected); -+ verify(mCallbacks).onError(eq(config.SSID)); -+ verify(mWifiConfigManager, atLeastOnce()).updateNetworkSelectionStatus(eq(config.networkId), -+ eq(WifiConfiguration.NetworkSelectionStatus -+ .DISABLED_BY_WIFI_MANAGER)); -+ } -+ -+ /** -+ * Verify that is reports errors if the server cert issuer does not match the parent subject. -+ */ -+ @Test -+ public void verifyOnErrorWithIncompleteChain() throws Exception { -+ assumeTrue(SdkLevel.isAtLeastT()); -+ boolean isAtLeastT = true, isTrustOnFirstUseSupported = true, isUserSelected = true; -+ -+ WifiConfiguration config = prepareWifiConfiguration(isAtLeastT); -+ setupTest(config, isAtLeastT, isTrustOnFirstUseSupported); -+ -+ X509Certificate mockCaCert = generateMockCert("ca", "ca", true); -+ // Missing intermediate cert. -+ X509Certificate mockServerCert = generateMockCert("server", "intermediate", false); -+ mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 1, mockCaCert); -+ mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 0, mockServerCert); -+ -+ mInsecureEapNetworkHandler.startUserApprovalIfNecessary(isUserSelected); -+ verify(mCallbacks).onError(eq(config.SSID)); -+ verify(mWifiConfigManager, atLeastOnce()).updateNetworkSelectionStatus(eq(config.networkId), -+ eq(WifiConfiguration.NetworkSelectionStatus -+ .DISABLED_BY_WIFI_MANAGER)); -+ } -+ -+ /** -+ * Verify that setting pending certificate won't crash with no current configuration. -+ */ -+ @Test -+ public void verifySetPendingCertificateNoCrashWithNoConfig() -+ throws Exception { -+ assumeTrue(SdkLevel.isAtLeastT()); -+ mInsecureEapNetworkHandler = new InsecureEapNetworkHandler( -+ mContext, -+ mWifiConfigManager, -+ mWifiNative, -+ mFrameworkFacade, -+ mWifiNotificationManager, -+ mWifiDialogManager, -+ true /* isTrustOnFirstUseSupported */, -+ false /* isInsecureEnterpriseConfigurationAllowed */, -+ mCallbacks, -+ WIFI_IFACE_NAME, -+ mHandler); -+ X509Certificate mockSelfSignedCert = generateMockCert("self", "self", false); -+ mInsecureEapNetworkHandler.addPendingCertificate("NotExist", 0, mockSelfSignedCert); -+ } -+ -+ @Test -+ public void testExistingCertChainIsClearedOnPreparingNewConnection() throws Exception { -+ assumeTrue(SdkLevel.isAtLeastT()); -+ boolean isAtLeastT = true, isTrustOnFirstUseSupported = true, isUserSelected = true; -+ -+ WifiConfiguration config = prepareWifiConfiguration(isAtLeastT); -+ setupTest(config, isAtLeastT, isTrustOnFirstUseSupported); -+ -+ // Missing root CA cert. -+ mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 0, -+ generateMockCert("server", "ca", false)); -+ -+ // The wrong cert chain should be cleared after this call. -+ mInsecureEapNetworkHandler.prepareConnection(config); -+ -+ X509Certificate mockSelfSignedCert = generateMockCert("self", "self", false); -+ mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 0, mockSelfSignedCert); -+ -+ mInsecureEapNetworkHandler.startUserApprovalIfNecessary(isUserSelected); -+ verify(mCallbacks, never()).onError(any()); -+ } -+ -+ @Test -+ public void verifyUserApprovalIsNotNeededWithDifferentTargetConfig() throws Exception { -+ assumeTrue(SdkLevel.isAtLeastT()); -+ boolean isAtLeastT = true, isTrustOnFirstUseSupported = true, isUserSelected = true; -+ -+ WifiConfiguration config = prepareWifiConfiguration(isAtLeastT); -+ setupTest(config, isAtLeastT, isTrustOnFirstUseSupported); -+ -+ X509Certificate mockSelfSignedCert = generateMockCert("self", "self", false); -+ mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 0, mockSelfSignedCert); -+ -+ // Pass another PSK config which is not the same as the current one. -+ WifiConfiguration pskConfig = WifiConfigurationTestUtil.createPskNetwork(); -+ pskConfig.networkId = FRAMEWORK_NETWORK_ID + 2; -+ mInsecureEapNetworkHandler.prepareConnection(pskConfig); -+ mInsecureEapNetworkHandler.startUserApprovalIfNecessary(isUserSelected); -+ verify(mCallbacks, never()).onError(any()); -+ -+ // Pass another non-TOFU EAP config which is not the same as the current one. -+ WifiConfiguration anotherEapConfig = spy(WifiConfigurationTestUtil.createEapNetwork( -+ WifiEnterpriseConfig.Eap.SIM, WifiEnterpriseConfig.Phase2.NONE)); -+ anotherEapConfig.networkId = FRAMEWORK_NETWORK_ID + 1; -+ mInsecureEapNetworkHandler.prepareConnection(anotherEapConfig); -+ mInsecureEapNetworkHandler.startUserApprovalIfNecessary(isUserSelected); - verify(mCallbacks, never()).onError(any()); - } - - private void verifyTrustOnFirstUseFlowWithDefaultCerts(WifiConfiguration config, - int action, boolean isTrustOnFirstUseSupported, boolean isUserSelected, - boolean needUserApproval) throws Exception { -+ X509Certificate mockCaCert = generateMockCert("ca", "ca", true); -+ X509Certificate mockServerCert = generateMockCert("server", "middle", false); - if (isTrustOnFirstUseSupported) { -- mInsecureEapNetworkHandler.setPendingCertificate(config.SSID, 2, FakeKeys.CA_CERT0); -- mInsecureEapNetworkHandler.setPendingCertificate(config.SSID, 1, FakeKeys.CA_CERT1); -- mInsecureEapNetworkHandler.setPendingCertificate(config.SSID, 0, FakeKeys.CLIENT_CERT); -+ mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 2, mockCaCert); -+ mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 1, -+ generateMockCert("middle", "ca", false)); -+ mInsecureEapNetworkHandler.addPendingCertificate(config.SSID, 0, mockServerCert); - } - verifyTrustOnFirstUseFlow(config, action, isTrustOnFirstUseSupported, -- isUserSelected, needUserApproval, FakeKeys.CA_CERT0, FakeKeys.CLIENT_CERT); -+ isUserSelected, needUserApproval, mockCaCert, mockServerCert); - } - - private void verifyTrustOnFirstUseFlow(WifiConfiguration config, - int action, boolean isTrustOnFirstUseSupported, boolean isUserSelected, - boolean needUserApproval, X509Certificate expectedCaCert, - X509Certificate expectedServerCert) throws Exception { -- assertEquals(needUserApproval, -- mInsecureEapNetworkHandler.startUserApprovalIfNecessary(isUserSelected)); -+ mInsecureEapNetworkHandler.startUserApprovalIfNecessary(isUserSelected); - -+ ArgumentCaptor dialogMessageCaptor = ArgumentCaptor.forClass(String.class); - if (isUserSelected) { - ArgumentCaptor dialogCallbackCaptor = - ArgumentCaptor.forClass(WifiDialogManager.SimpleDialogCallback.class); - verify(mWifiDialogManager).createSimpleDialogWithUrl( -- any(), any(), any(), anyInt(), anyInt(), any(), any(), any(), -- dialogCallbackCaptor.capture(), any()); -+ any(), dialogMessageCaptor.capture(), any(), anyInt(), anyInt(), any(), any(), -+ any(), dialogCallbackCaptor.capture(), any()); -+ if (isTrustOnFirstUseSupported) { -+ assertTofuDialogMessage(expectedCaCert, expectedServerCert, -+ dialogMessageCaptor.getValue()); -+ } - if (action == ACTION_ACCEPT) { - dialogCallbackCaptor.getValue().onPositiveButtonClicked(); - } else if (action == ACTION_REJECT) { -@@ -533,6 +739,7 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - verify(mFrameworkFacade, never()).makeAlertDialogBuilder(any()); - verify(mFrameworkFacade).makeNotificationBuilder( - eq(mContext), eq(WifiService.NOTIFICATION_NETWORK_ALERTS)); -+ - // Trust On First Use notification has no accept and reject action buttons. - // It only supports TAP and launch the dialog. - if (isTrustOnFirstUseSupported) { -@@ -543,8 +750,10 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - ArgumentCaptor dialogCallbackCaptor = - ArgumentCaptor.forClass(WifiDialogManager.SimpleDialogCallback.class); - verify(mWifiDialogManager).createSimpleDialogWithUrl( -- any(), any(), any(), anyInt(), anyInt(), any(), any(), any(), -- dialogCallbackCaptor.capture(), any()); -+ any(), dialogMessageCaptor.capture(), any(), anyInt(), anyInt(), any(), -+ any(), any(), dialogCallbackCaptor.capture(), any()); -+ assertTofuDialogMessage(expectedCaCert, expectedServerCert, -+ dialogMessageCaptor.getValue()); - if (action == ACTION_ACCEPT) { - dialogCallbackCaptor.getValue().onPositiveButtonClicked(); - } else if (action == ACTION_REJECT) { -@@ -566,7 +775,8 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - } - - if (action == ACTION_ACCEPT) { -- verify(mWifiConfigManager).allowAutojoin(eq(config.networkId), eq(true)); -+ verify(mWifiConfigManager).updateNetworkSelectionStatus(eq(config.networkId), -+ eq(WifiConfiguration.NetworkSelectionStatus.DISABLED_NONE)); - if (isTrustOnFirstUseSupported) { - verify(mWifiConfigManager).updateCaCertificate( - eq(config.networkId), eq(expectedCaCert), eq(expectedServerCert)); -@@ -576,8 +786,11 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - } - verify(mCallbacks).onAccept(eq(config.SSID)); - } else if (action == ACTION_REJECT) { -- verify(mWifiConfigManager).allowAutojoin(eq(config.networkId), eq(false)); -- verify(mCallbacks).onReject(eq(config.SSID)); -+ verify(mWifiConfigManager, atLeastOnce()) -+ .updateNetworkSelectionStatus(eq(config.networkId), -+ eq(WifiConfiguration.NetworkSelectionStatus -+ .DISABLED_BY_WIFI_MANAGER)); -+ verify(mCallbacks).onReject(eq(config.SSID), eq(!isTrustOnFirstUseSupported)); - } else if (action == ACTION_TAP) { - verify(mWifiDialogManager).createSimpleDialogWithUrl( - any(), any(), any(), anyInt(), anyInt(), any(), any(), any(), any(), any()); -@@ -586,4 +799,47 @@ public class InsecureEapNetworkHandlerTest extends WifiBaseTest { - verify(mCallbacks, never()).onError(any()); - } - -+ private void assertTofuDialogMessage( -+ X509Certificate rootCaCert, -+ X509Certificate serverCert, -+ String message) { -+ CertificateSubjectInfo serverCertSubjectInfo = -+ CertificateSubjectInfo.parse(serverCert.getSubjectX500Principal().getName()); -+ CertificateSubjectInfo serverCertIssuerInfo = -+ CertificateSubjectInfo.parse(serverCert.getIssuerX500Principal().getName()); -+ assertNotNull("Server cert subject info is null", serverCertSubjectInfo); -+ assertNotNull("Server cert issuer info is null", serverCertIssuerInfo); -+ -+ assertTrue("TOFU dialog message does not contain server cert subject name ", -+ message.contains(serverCertSubjectInfo.commonName)); -+ assertTrue("TOFU dialog message does not contain server cert issuer name", -+ message.contains(serverCertIssuerInfo.commonName)); -+ if (!TextUtils.isEmpty(serverCertSubjectInfo.organization)) { -+ assertTrue("TOFU dialog message does not contain server cert organization", -+ message.contains(serverCertSubjectInfo.organization)); -+ } -+ } -+ -+ @Test -+ public void testCleanUp() throws Exception { -+ assumeTrue(SdkLevel.isAtLeastT()); -+ -+ boolean isAtLeastT = true, isTrustOnFirstUseSupported = true; -+ WifiConfiguration config = prepareWifiConfiguration(isAtLeastT); -+ setupTest(config, isAtLeastT, isTrustOnFirstUseSupported); -+ -+ BroadcastReceiver br = mBroadcastReceiverCaptor.getValue(); -+ mInsecureEapNetworkHandler.cleanup(); -+ verify(mContext).unregisterReceiver(br); -+ } -+ -+ /** -+ * Verify the getDigest and fingerprint methods -+ */ -+ @Test -+ public void verifyGetDigest() throws Exception { -+ X509Certificate mockServerCert = generateMockCert("server", "ca", false); -+ assertEquals(mInsecureEapNetworkHandler.getDigest(mockServerCert, "SHA256"), -+ TEST_EXPECTED_SHA_256_SIGNATURE); -+ } - } -diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java -index efb62b127f..f8c9123694 100644 ---- a/service/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java -+++ b/service/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java -@@ -45,6 +45,7 @@ import static org.mockito.Mockito.verify; - import static org.mockito.Mockito.when; - import static org.mockito.Mockito.withSettings; - -+import android.Manifest; - import android.annotation.Nullable; - import android.app.ActivityManager; - import android.app.test.MockAnswerUtil.AnswerWithArguments; -@@ -7665,4 +7666,72 @@ public class WifiConfigManagerTest extends WifiBaseTest { - assertEquals(TEST_UPDATE_NAME, mWifiConfigManager - .getConfiguredNetwork(openNetId).creatorName); - } -+ -+ /** -+ * Verify that if the caller has NETWORK_SETTINGS permission, and the overlay -+ * config_wifiAllowInsecureEnterpriseConfigurationsForSettingsAndSUW is set, then it can add an -+ * insecure Enterprise network, with Root CA certificate not set and/or domain name not set. -+ */ -+ @Test -+ public void testAddInsecureEnterpriseNetworkWithNetworkSettingsPerm() throws Exception { -+ when(mContext.checkPermission(eq(android.Manifest.permission.NETWORK_SETTINGS), -+ anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED); -+ when(mContext.checkPermission(eq(Manifest.permission.NETWORK_SETUP_WIZARD), -+ anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_DENIED); -+ -+ // First set flag to not allow -+ when(mWifiGlobals.isInsecureEnterpriseConfigurationAllowed()).thenReturn(false); -+ -+ // Create an insecure Enterprise network -+ WifiConfiguration config = WifiConfigurationTestUtil.createEapNetwork(); -+ config.enterpriseConfig.setCaPath(null); -+ config.enterpriseConfig.setDomainSuffixMatch(null); -+ assertFalse(config.enterpriseConfig.isUserApproveNoCaCert()); -+ -+ // Verify operation fails -+ NetworkUpdateResult result = addNetworkToWifiConfigManager(config); -+ assertFalse(result.isSuccess()); -+ -+ // Set flag to allow -+ when(mWifiGlobals.isInsecureEnterpriseConfigurationAllowed()).thenReturn(true); -+ -+ // Verify operation succeeds -+ NetworkUpdateResult res = addNetworkToWifiConfigManager(config); -+ assertTrue(res.isSuccess()); -+ config = mWifiConfigManager.getConfiguredNetwork(res.getNetworkId()); -+ assertNotNull(config); -+ assertTrue(config.enterpriseConfig.isUserApproveNoCaCert()); -+ } -+ -+ /** -+ * Verify that if the caller does NOT have NETWORK_SETTINGS permission, then it cannot add an -+ * insecure Enterprise network, with Root CA certificate not set and/or domain name not set, -+ * regardless of the overlay config_wifiAllowInsecureEnterpriseConfigurationsForSettingsAndSUW -+ * value. -+ */ -+ @Test -+ public void testAddInsecureEnterpriseNetworkWithNoNetworkSettingsPerm() throws Exception { -+ when(mContext.checkPermission(eq(android.Manifest.permission.NETWORK_SETTINGS), -+ anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_DENIED); -+ when(mContext.checkPermission(eq(Manifest.permission.NETWORK_SETUP_WIZARD), -+ anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_DENIED); -+ -+ // First set flag to not allow -+ when(mWifiGlobals.isInsecureEnterpriseConfigurationAllowed()).thenReturn(false); -+ -+ // Create an insecure Enterprise network -+ WifiConfiguration config = WifiConfigurationTestUtil.createEapNetwork(); -+ config.enterpriseConfig.setCaPath(null); -+ config.enterpriseConfig.setDomainSuffixMatch(null); -+ -+ // Verify operation fails -+ NetworkUpdateResult result = addNetworkToWifiConfigManager(config); -+ assertFalse(result.isSuccess()); -+ -+ // Set flag to allow -+ when(mWifiGlobals.isInsecureEnterpriseConfigurationAllowed()).thenReturn(true); -+ -+ // Verify operation still fails -+ assertFalse(addNetworkToWifiConfigManager(config).isSuccess()); -+ } - } -diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiConfigurationTestUtil.java b/service/tests/wifitests/src/com/android/server/wifi/WifiConfigurationTestUtil.java -index eefa46bb7c..9dc610b275 100644 ---- a/service/tests/wifitests/src/com/android/server/wifi/WifiConfigurationTestUtil.java -+++ b/service/tests/wifitests/src/com/android/server/wifi/WifiConfigurationTestUtil.java -@@ -191,6 +191,7 @@ public class WifiConfigurationTestUtil { - if ((security & SECURITY_EAP_SUITE_B) != 0) { - config.addSecurityParams( - WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE_192_BIT); -+ config.enterpriseConfig.setDomainSuffixMatch(TEST_DOM_SUBJECT_MATCH); - } - - if ((security & SECURITY_WAPI_PSK) != 0) { -diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiKeyStoreTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiKeyStoreTest.java -index 9de443def4..37fbb8f7b2 100644 ---- a/service/tests/wifitests/src/com/android/server/wifi/WifiKeyStoreTest.java -+++ b/service/tests/wifitests/src/com/android/server/wifi/WifiKeyStoreTest.java -@@ -307,6 +307,37 @@ public class WifiKeyStoreTest extends WifiBaseTest { - savedNetwork.allowedSuiteBCiphers.get(WifiConfiguration.SuiteBCipher.ECDHE_ECDSA)); - } - -+ /** -+ * Test configuring WPA3-Enterprise in 192-bit mode for RSA 3072 correctly when CA and client -+ * certificates are of RSA 3072 type and the network is Suite-B. -+ */ -+ @Test -+ public void testConfigureSuiteBRsa3072UserAndTofu() throws Exception { -+ // No Root CA certificates, but TOFU is on - Setting the suite-b mode by the user cert -+ when(mWifiEnterpriseConfig.getCaCertificateAliases()) -+ .thenReturn(null); -+ when(mWifiEnterpriseConfig.getCaCertificate()).thenReturn(null); -+ when(mWifiEnterpriseConfig.getCaCertificates()) -+ .thenReturn(null); -+ when(mWifiEnterpriseConfig.isTrustOnFirstUseEnabled()).thenReturn(true); -+ -+ when(mWifiEnterpriseConfig.isEapMethodServerCertUsed()).thenReturn(true); -+ when(mWifiEnterpriseConfig.getClientPrivateKey()) -+ .thenReturn(FakeKeys.CLIENT_SUITE_B_RSA3072_KEY); -+ when(mWifiEnterpriseConfig.getClientCertificate()).thenReturn( -+ FakeKeys.CLIENT_SUITE_B_RSA3072_CERT); -+ when(mWifiEnterpriseConfig.getClientCertificateChain()) -+ .thenReturn(new X509Certificate[]{FakeKeys.CLIENT_SUITE_B_RSA3072_CERT}); -+ when(mKeyStore.getCertificate(eq(USER_CERT_ALIAS))).thenReturn( -+ FakeKeys.CLIENT_SUITE_B_RSA3072_CERT); -+ WifiConfiguration savedNetwork = WifiConfigurationTestUtil.createEapSuiteBNetwork( -+ WifiConfiguration.SuiteBCipher.ECDHE_RSA); -+ savedNetwork.enterpriseConfig = mWifiEnterpriseConfig; -+ assertTrue(mWifiKeyStore.updateNetworkKeys(savedNetwork, null)); -+ assertTrue(savedNetwork.allowedSuiteBCiphers.get(WifiConfiguration.SuiteBCipher.ECDHE_RSA)); -+ } -+ -+ - /** - * Test configuring WPA3-Enterprise in 192-bit mode for RSA 3072 fails when CA and client - * certificates are not of the same type. diff --git a/Patches/LineageOS-20.0/android_packages_services_Telecomm/ASB-2023-06/0001-Call-Redirection-unbind-service-when-onBind-returns-.patch b/Patches/LineageOS-20.0/android_packages_services_Telecomm/ASB-2023-06/0001-Call-Redirection-unbind-service-when-onBind-returns-.patch deleted file mode 100644 index 4b753393..00000000 --- a/Patches/LineageOS-20.0/android_packages_services_Telecomm/ASB-2023-06/0001-Call-Redirection-unbind-service-when-onBind-returns-.patch +++ /dev/null @@ -1,79 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Pranav Madapurmath -Date: Tue, 21 Mar 2023 23:28:56 +0000 -Subject: [PATCH] Call Redirection: unbind service when onBind returns null - -The call redirection service does not handle the corner case of -onNullBinding (occurs when onBind returns null). This vulnerability -would give the app that has the call redirection role unintentional -access to launch background activities outside the scope of a call -lifecycle. - -Fixes: 273260090 -Test: Unit test to ensure we unbind the service on null onBind -Test: CTS for similar assertion -(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:9b753a1d0d1e563b3363ecd49f7f9d0100200cab) -Merged-In: Ib9d44d239833131eb055e83801cb635e8efe0b81 -Change-Id: Ib9d44d239833131eb055e83801cb635e8efe0b81 ---- - .../CallRedirectionProcessor.java | 13 ++++++++++ - .../tests/CallRedirectionProcessorTest.java | 24 +++++++++++++++++++ - 2 files changed, 37 insertions(+) - -diff --git a/src/com/android/server/telecom/callredirection/CallRedirectionProcessor.java b/src/com/android/server/telecom/callredirection/CallRedirectionProcessor.java -index adeb3113d..226382bde 100644 ---- a/src/com/android/server/telecom/callredirection/CallRedirectionProcessor.java -+++ b/src/com/android/server/telecom/callredirection/CallRedirectionProcessor.java -@@ -162,6 +162,19 @@ public class CallRedirectionProcessor implements CallRedirectionCallback { - Log.endSession(); - } - } -+ -+ @Override -+ public void onNullBinding(ComponentName componentName) { -+ // Make sure we unbind the service if onBind returns null -+ Log.startSession("CRSC.oNB"); -+ try { -+ synchronized (mTelecomLock) { -+ finishCallRedirection(); -+ } -+ } finally { -+ Log.endSession(); -+ } -+ } - } - - private class CallRedirectionAdapter extends ICallRedirectionAdapter.Stub { -diff --git a/tests/src/com/android/server/telecom/tests/CallRedirectionProcessorTest.java b/tests/src/com/android/server/telecom/tests/CallRedirectionProcessorTest.java -index 0a896a8ce..f2fe045ef 100644 ---- a/tests/src/com/android/server/telecom/tests/CallRedirectionProcessorTest.java -+++ b/tests/src/com/android/server/telecom/tests/CallRedirectionProcessorTest.java -@@ -352,4 +352,28 @@ public class CallRedirectionProcessorTest extends TelecomTestCase { - assertEquals(REDIRECTED_GATEWAY_NUMBER_WITH_POST_DIAL, - gatewayInfoArgumentCaptor.getValue().getGatewayAddress()); - } -+ -+ @Test -+ public void testUnbindOnNullBind() throws Exception { -+ startProcessWithNoGateWayInfo(); -+ // To make sure tests are not flaky, clean all the previous handler messages -+ waitForHandlerAction(mProcessor.getHandler(), HANDLER_TIMEOUT_DELAY); -+ enableUserDefinedCallRedirectionService(); -+ disableCarrierCallRedirectionService(); -+ -+ mProcessor.performCallRedirection(); -+ -+ // Capture the binder -+ ArgumentCaptor serviceConnectionCaptor = ArgumentCaptor.forClass( -+ ServiceConnection.class); -+ // Verify binding occurred -+ verify(mContext, times(1)).bindServiceAsUser(any(Intent.class), -+ serviceConnectionCaptor.capture(), anyInt(), eq(UserHandle.CURRENT)); -+ // Simulate null return from onBind -+ serviceConnectionCaptor.getValue().onNullBinding(USER_DEFINED_SERVICE_TEST_COMPONENT_NAME); -+ -+ // Verify service was unbound -+ verify(mContext, times(1)). -+ unbindService(any(ServiceConnection.class)); -+ } - } diff --git a/Scripts/LineageOS-20.0/Patch.sh b/Scripts/LineageOS-20.0/Patch.sh index b11e3ee3..45f1bf93 100644 --- a/Scripts/LineageOS-20.0/Patch.sh +++ b/Scripts/LineageOS-20.0/Patch.sh @@ -97,7 +97,6 @@ applyPatch "$DOS_PATCHES/android_build/0004-Selective_APEX.patch"; #Only enable sed -i '75i$(my_res_package): PRIVATE_AAPT_FLAGS += --auto-add-overlay' core/aapt2.mk; #Enable auto-add-overlay for packages, this allows the vendor overlay to easily work across all branches. sed -i 's/PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION := 23/PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION := 28/' core/version_util.mk; #Set the minimum supported target SDK to Pie (GrapheneOS) #sed -i 's/PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS := true/PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS := false/' core/product_config.mk; #broken by hardenDefconfig -sed -i 's/2023-05-05/2023-06-01/' core/version_defaults.mk; #Bump Security String #T_asb_2023-06 #XXX fi; if enterAndClear "build/soong"; then @@ -123,12 +122,7 @@ sed -i 's/34359738368/2147483648/' Android.bp; #revert 48-bit address space requ fi; fi; -if enterAndClear "frameworks/av"; then -git am $DOS_PATCHES/android_frameworks_av/ASB-2023-06/*.patch; #T_asb_2023-06 -fi; - if enterAndClear "frameworks/base"; then -git am $DOS_PATCHES/android_frameworks_base/ASB-2023-06/*.patch; #T_asb_2023-06 git revert --no-edit d36faad3267522c6d3ff91ba9dcca8f6274bccd1; #Reverts "JobScheduler: Respect allow-in-power-save perm" in favor of below patch git revert --no-edit 90d6826548189ca850d91692e71fcc1be426f453; #Reverts "Remove sensitive info from SUPL requests" in favor of below patch applyPatch "$DOS_PATCHES/android_frameworks_base/0007-Always_Restict_Serial.patch"; #Always restrict access to Build.SERIAL (GrapheneOS) @@ -291,7 +285,6 @@ if [ "$DOS_GRAPHENE_CONSTIFY" = true ]; then applyPatch "$DOS_PATCHES/android_pa fi; if enterAndClear "packages/apps/Settings"; then -git am $DOS_PATCHES/android_packages_apps_Settings/ASB-2023-06/*.patch; #T_asb_2023-06 git revert --no-edit 41b4ed345a91da1dd46c00ee11a151c2b5ff4f43; applyPatch "$DOS_PATCHES/android_packages_apps_Settings/0004-Private_DNS.patch"; #More 'Private DNS' options (heavily based off of a CalyxOS patch) applyPatch "$DOS_PATCHES/android_packages_apps_Settings/0005-Automatic_Reboot.patch"; #Timeout for reboot (GrapheneOS) @@ -316,10 +309,6 @@ if enterAndClear "packages/apps/ThemePicker"; then git revert --no-edit fcf658d2005dc557a95d5a7fb89cb90d06b31d33; #grant permission by default, to prevent crashes, missing previews, and confusion fi; -if enterAndClear "packages/apps/Traceur"; then -git am $DOS_PATCHES/android_packages_apps_Traceur/ASB-2023-06/*.patch; #T_asb_2023-06 -fi; - if enterAndClear "packages/apps/Trebuchet"; then cp $DOS_BUILD_BASE/vendor/divested/overlay/common/packages/apps/Trebuchet/res/xml/default_workspace_*.xml res/xml/; #XXX: Likely no longer needed fi; @@ -336,10 +325,6 @@ applyPatch "$DOS_PATCHES_COMMON/android_packages_inputmethods_LatinIME/0001-Voic applyPatch "$DOS_PATCHES_COMMON/android_packages_inputmethods_LatinIME/0002-Disable_Personalization.patch"; #Disable personalization dictionary by default (GrapheneOS) fi; -if enterAndClear "packages/modules/Bluetooth"; then -git am $DOS_PATCHES/android_packages_modules_Bluetooth/ASB-2023-06/*.patch; #T_asb_2023-06 -fi; - if enterAndClear "packages/modules/Connectivity"; then applyPatch "$DOS_PATCHES/android_packages_modules_Connectivity/0001-Network_Permission-1.patch"; #Skip reportNetworkConnectivity() when permission is revoked (GrapheneOS) applyPatch "$DOS_PATCHES/android_packages_modules_Connectivity/0001-Network_Permission-2.patch"; #Enforce INTERNET permission per-uid instead of per-appId (GrapheneOS) @@ -365,7 +350,6 @@ applyPatch "$DOS_PATCHES/android_packages_modules_Permission/0006-Location_Indic fi; if enterAndClear "packages/modules/Wifi"; then -git am $DOS_PATCHES/android_packages_modules_Wifi/ASB-2023-06/*.patch; #T_asb_2023-06 applyPatch "$DOS_PATCHES/android_packages_modules_Wifi/344228.patch"; #wifi: resurrect mWifiLinkLayerStatsSupported counter (sassmann) applyPatch "$DOS_PATCHES/android_packages_modules_Wifi/0001-Random_MAC.patch"; #Add support for always generating new random MAC (GrapheneOS) fi; @@ -374,10 +358,6 @@ if enterAndClear "packages/providers/DownloadProvider"; then applyPatch "$DOS_PATCHES/android_packages_providers_DownloadProvider/0001-Network_Permission.patch"; #Expose the NETWORK permission (GrapheneOS) fi; -if enterAndClear "packages/services/Telecomm"; then -git am $DOS_PATCHES/android_packages_services_Telecomm/ASB-2023-06/*.patch; #T_asb_2023-06 -fi; - #if enterAndClear "packages/providers/TelephonyProvider"; then #cp $DOS_PATCHES_COMMON/android_packages_providers_TelephonyProvider/carrier_list.* assets/latest_carrier_id/; #fi;