From bce91d9418823c129e5b47b103c3bfdb22392969 Mon Sep 17 00:00:00 2001 From: Christine Franks Date: Tue, 3 Jul 2018 14:44:08 -0700 Subject: [PATCH 1/3] Move color management check to ColorDisplayManager Bug: 111215474 Test: make RunSettingsRoboTests -j100 Change-Id: I8ab675a49e119014e59fcfdb61fcd9253e8c0da1 --- AndroidManifest.xml | 1 + .../ColorModePreferenceController.java | 41 ++----------------- ...htDisplayAutoModePreferenceController.java | 1 - 3 files changed, 4 insertions(+), 39 deletions(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index b69cac73ad3..943e7225798 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -69,6 +69,7 @@ + diff --git a/src/com/android/settings/display/ColorModePreferenceController.java b/src/com/android/settings/display/ColorModePreferenceController.java index 46794364156..5d6a4128768 100644 --- a/src/com/android/settings/display/ColorModePreferenceController.java +++ b/src/com/android/settings/display/ColorModePreferenceController.java @@ -14,11 +14,7 @@ package com.android.settings.display; import android.content.Context; -import android.os.IBinder; -import android.os.Parcel; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.util.Log; +import android.hardware.display.ColorDisplayManager; import androidx.annotation.VisibleForTesting; @@ -29,19 +25,16 @@ import com.android.settings.core.BasePreferenceController; public class ColorModePreferenceController extends BasePreferenceController { private static final String TAG = "ColorModePreference"; - private static final int SURFACE_FLINGER_TRANSACTION_QUERY_COLOR_MANAGEMENT = 1030; - - private final ConfigurationWrapper mConfigWrapper; private ColorDisplayController mColorDisplayController; public ColorModePreferenceController(Context context, String key) { super(context, key); - mConfigWrapper = new ConfigurationWrapper(); } @Override public int getAvailabilityStatus() { - return mConfigWrapper.isDeviceColorManaged() + return mContext.getSystemService(ColorDisplayManager.class) + .isDeviceColorManaged() && !getColorDisplayController().getAccessibilityTransformActivated() ? AVAILABLE_UNSEARCHABLE : DISABLED_FOR_USER; } @@ -68,32 +61,4 @@ public class ColorModePreferenceController extends BasePreferenceController { } return mColorDisplayController; } - - @VisibleForTesting - static class ConfigurationWrapper { - private final IBinder mSurfaceFlinger; - - ConfigurationWrapper() { - mSurfaceFlinger = ServiceManager.getService("SurfaceFlinger"); - } - - boolean isDeviceColorManaged() { - if (mSurfaceFlinger != null) { - final Parcel data = Parcel.obtain(); - final Parcel reply = Parcel.obtain(); - data.writeInterfaceToken("android.ui.ISurfaceComposer"); - try { - mSurfaceFlinger.transact(SURFACE_FLINGER_TRANSACTION_QUERY_COLOR_MANAGEMENT, - data, reply, 0); - return reply.readBoolean(); - } catch (RemoteException ex) { - Log.e(TAG, "Failed to query color management support", ex); - } finally { - data.recycle(); - reply.recycle(); - } - } - return false; - } - } } diff --git a/src/com/android/settings/display/NightDisplayAutoModePreferenceController.java b/src/com/android/settings/display/NightDisplayAutoModePreferenceController.java index 8ad5e630e2b..1710f515160 100644 --- a/src/com/android/settings/display/NightDisplayAutoModePreferenceController.java +++ b/src/com/android/settings/display/NightDisplayAutoModePreferenceController.java @@ -17,7 +17,6 @@ package com.android.settings.display; import android.content.Context; - import android.hardware.display.ColorDisplayManager; import androidx.preference.DropDownPreference; From 2efa93460c9ed997af3b179e84fe1a8bd8f75846 Mon Sep 17 00:00:00 2001 From: Yi-Ling Chuang Date: Thu, 29 Nov 2018 18:27:56 +0800 Subject: [PATCH 2/3] Reset dismissal UI to the baseline when exiting the page. Flip the card back to slice view if users exit the page instead of getting the dismissal UI preserved. We should also reset the UI to the baseline if users dismiss a card, so when the view is reused, slice view can then be displayed correctly. Change-Id: If5448c57dec44ef5c1d3472358d4ddc9398538b8 Fixes: 119820168 Test: robotests --- .../slices/SliceContextualCardController.java | 4 +- .../slices/SliceContextualCardRenderer.java | 30 ++++++- .../SliceContextualCardRendererTest.java | 86 +++++++++++++++---- 3 files changed, 95 insertions(+), 25 deletions(-) diff --git a/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardController.java b/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardController.java index 4378be3f0d7..8720a3ce488 100644 --- a/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardController.java +++ b/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardController.java @@ -69,9 +69,9 @@ public class SliceContextualCardController implements ContextualCardController { dbHelper.markContextualCardAsDismissed(mContext, card.getName()); }); showFeedbackDialog(card); - final ContextualCardFeatureProvider contexualCardFeatureProvider = + final ContextualCardFeatureProvider contextualCardFeatureProvider = FeatureFactory.getFactory(mContext).getContextualCardFeatureProvider(); - contexualCardFeatureProvider.logContextualCardDismiss(mContext, card); + contextualCardFeatureProvider.logContextualCardDismiss(mContext, card); } @Override diff --git a/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRenderer.java b/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRenderer.java index a2d6e2b119c..4df2a04cd9e 100644 --- a/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRenderer.java +++ b/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRenderer.java @@ -28,8 +28,11 @@ import android.widget.ViewFlipper; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleObserver; import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LiveData; +import androidx.lifecycle.OnLifecycleEvent; import androidx.recyclerview.widget.RecyclerView; import androidx.slice.Slice; import androidx.slice.SliceItem; @@ -51,13 +54,15 @@ import java.util.Set; * Card renderer for {@link ContextualCard} built as slices. */ public class SliceContextualCardRenderer implements ContextualCardRenderer, - SliceView.OnSliceActionListener { + SliceView.OnSliceActionListener, LifecycleObserver { public static final int VIEW_TYPE = R.layout.homepage_slice_tile; private static final String TAG = "SliceCardRenderer"; @VisibleForTesting final Map> mSliceLiveDataMap; + @VisibleForTesting + final Set mFlippedCardSet; private final Context mContext; private final LifecycleOwner mLifecycleOwner; @@ -71,6 +76,8 @@ public class SliceContextualCardRenderer implements ContextualCardRenderer, mSliceLiveDataMap = new ArrayMap<>(); mControllerRendererPool = controllerRendererPool; mCardSet = new ArraySet<>(); + mFlippedCardSet = new ArraySet<>(); + mLifecycleOwner.getLifecycle().addObserver(this); } @Override @@ -122,20 +129,23 @@ public class SliceContextualCardRenderer implements ContextualCardRenderer, } private void initDismissalActions(SliceViewHolder cardHolder, ContextualCard card) { - final ViewFlipper viewFlipper = cardHolder.itemView.findViewById(R.id.viewFlipper); cardHolder.sliceView.setOnLongClickListener(v -> { - viewFlipper.showNext(); + cardHolder.viewFlipper.showNext(); + mFlippedCardSet.add(cardHolder); return true; }); final Button btnKeep = cardHolder.itemView.findViewById(R.id.keep); btnKeep.setOnClickListener(v -> { - viewFlipper.showPrevious(); + cardHolder.resetCard(); + mFlippedCardSet.remove(cardHolder); }); final Button btnRemove = cardHolder.itemView.findViewById(R.id.remove); btnRemove.setOnClickListener(v -> { mControllerRendererPool.getController(mContext, card.getCardType()).onDismissed(card); + cardHolder.resetCard(); + mFlippedCardSet.remove(cardHolder); }); } @@ -158,12 +168,24 @@ public class SliceContextualCardRenderer implements ContextualCardRenderer, } } + @OnLifecycleEvent(Lifecycle.Event.ON_STOP) + public void onStop() { + mFlippedCardSet.stream().forEach(holder -> holder.resetCard()); + mFlippedCardSet.clear(); + } + public static class SliceViewHolder extends RecyclerView.ViewHolder { public final SliceView sliceView; + public final ViewFlipper viewFlipper; public SliceViewHolder(View view) { super(view); sliceView = view.findViewById(R.id.slice_view); + viewFlipper = view.findViewById(R.id.viewFlipper); + } + + public void resetCard() { + viewFlipper.setDisplayedChild(0); } } } diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRendererTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRendererTest.java index 13a2bc5ba87..11d0106565c 100644 --- a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRendererTest.java +++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRendererTest.java @@ -18,6 +18,7 @@ package com.android.settings.homepage.contextualcards.slices; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.verify; import android.app.Activity; @@ -51,10 +52,14 @@ import org.robolectric.android.controller.ActivityController; @RunWith(SettingsRobolectricTestRunner.class) public class SliceContextualCardRendererTest { + private static final String TEST_SLICE_URI = "content://test/test"; + @Mock private LiveData mSliceLiveData; @Mock private ControllerRendererPool mControllerRendererPool; + @Mock + private SliceContextualCardController mController; private Activity mActivity; private SliceContextualCardRenderer mRenderer; @@ -75,10 +80,9 @@ public class SliceContextualCardRendererTest { @Test public void bindView_shouldSetScrollableToFalse() { - final String sliceUri = "content://com.android.settings.slices/action/flashlight"; RecyclerView.ViewHolder viewHolder = getSliceViewHolder(); - mRenderer.bindView(viewHolder, buildContextualCard(sliceUri)); + mRenderer.bindView(viewHolder, buildContextualCard(TEST_SLICE_URI)); assertThat( ((SliceContextualCardRenderer.SliceViewHolder) viewHolder).sliceView.isScrollable @@ -99,64 +103,107 @@ public class SliceContextualCardRendererTest { @Test public void bindView_newSliceLiveData_shouldAddDataToMap() { - final String sliceUri = "content://com.android.settings.slices/action/flashlight"; - - mRenderer.bindView(getSliceViewHolder(), buildContextualCard(sliceUri)); + mRenderer.bindView(getSliceViewHolder(), buildContextualCard(TEST_SLICE_URI)); assertThat(mRenderer.mSliceLiveDataMap.size()).isEqualTo(1); } @Test public void bindView_sliceLiveDataShouldObserveSliceView() { - final String sliceUri = "content://com.android.settings.slices/action/flashlight"; + mRenderer.bindView(getSliceViewHolder(), buildContextualCard(TEST_SLICE_URI)); - mRenderer.bindView(getSliceViewHolder(), buildContextualCard(sliceUri)); - - assertThat(mRenderer.mSliceLiveDataMap.get(sliceUri).hasObservers()).isTrue(); + assertThat(mRenderer.mSliceLiveDataMap.get(TEST_SLICE_URI).hasObservers()).isTrue(); } @Test public void bindView_sliceLiveDataShouldRemoveObservers() { - final String sliceUri = "content://com.android.settings.slices/action/flashlight"; - mRenderer.mSliceLiveDataMap.put(sliceUri, mSliceLiveData); + mRenderer.mSliceLiveDataMap.put(TEST_SLICE_URI, mSliceLiveData); - mRenderer.bindView(getSliceViewHolder(), buildContextualCard(sliceUri)); + mRenderer.bindView(getSliceViewHolder(), buildContextualCard(TEST_SLICE_URI)); verify(mSliceLiveData).removeObservers(mLifecycleOwner); } @Test public void longClick_shouldFlipCard() { - final String sliceUri = "content://com.android.settings.slices/action/flashlight"; final RecyclerView.ViewHolder viewHolder = getSliceViewHolder(); final View card = viewHolder.itemView.findViewById(R.id.slice_view); final ViewFlipper viewFlipper = viewHolder.itemView.findViewById(R.id.viewFlipper); final View dismissalView = viewHolder.itemView.findViewById(R.id.dismissal_view); - mRenderer.bindView(viewHolder, buildContextualCard(sliceUri)); + mRenderer.bindView(viewHolder, buildContextualCard(TEST_SLICE_URI)); - assertThat(card).isNotNull(); card.performLongClick(); assertThat(viewFlipper.getCurrentView()).isEqualTo(dismissalView); } + @Test + public void longClick_shouldAddViewHolderToSet() { + final RecyclerView.ViewHolder viewHolder = getSliceViewHolder(); + final View card = viewHolder.itemView.findViewById(R.id.slice_view); + mRenderer.bindView(viewHolder, buildContextualCard(TEST_SLICE_URI)); + + card.performLongClick(); + + assertThat(mRenderer.mFlippedCardSet).contains(viewHolder); + } + @Test public void viewClick_keepCard_shouldFlipBackToSlice() { - final String sliceUri = "content://com.android.settings.slices/action/flashlight"; final RecyclerView.ViewHolder viewHolder = getSliceViewHolder(); final View card = viewHolder.itemView.findViewById(R.id.slice_view); final Button btnKeep = viewHolder.itemView.findViewById(R.id.keep); final ViewFlipper viewFlipper = viewHolder.itemView.findViewById(R.id.viewFlipper); - mRenderer.bindView(viewHolder, buildContextualCard(sliceUri)); + mRenderer.bindView(viewHolder, buildContextualCard(TEST_SLICE_URI)); - assertThat(card).isNotNull(); card.performLongClick(); - assertThat(btnKeep).isNotNull(); btnKeep.performClick(); assertThat(viewFlipper.getCurrentView()).isInstanceOf(SliceView.class); } + @Test + public void viewClick_keepCard_shouldRemoveViewHolderFromSet() { + final RecyclerView.ViewHolder viewHolder = getSliceViewHolder(); + final View card = viewHolder.itemView.findViewById(R.id.slice_view); + final Button btnKeep = viewHolder.itemView.findViewById(R.id.keep); + mRenderer.bindView(viewHolder, buildContextualCard(TEST_SLICE_URI)); + + card.performLongClick(); + btnKeep.performClick(); + + assertThat(mRenderer.mFlippedCardSet).doesNotContain(viewHolder); + } + + @Test + public void viewClick_removeCard_shouldRemoveViewHolderFromSet() { + final RecyclerView.ViewHolder viewHolder = getSliceViewHolder(); + final View card = viewHolder.itemView.findViewById(R.id.slice_view); + final Button btnRemove = viewHolder.itemView.findViewById(R.id.remove); + final ContextualCard contextualCard = buildContextualCard(TEST_SLICE_URI); + mRenderer.bindView(viewHolder, contextualCard); + doReturn(mController).when(mControllerRendererPool).getController(mActivity, + ContextualCard.CardType.SLICE); + + card.performLongClick(); + btnRemove.performClick(); + + assertThat(mRenderer.mFlippedCardSet).doesNotContain(viewHolder); + } + + @Test + public void onStop_cardIsFlipped_shouldFlipBack() { + final RecyclerView.ViewHolder viewHolder = getSliceViewHolder(); + final View card = viewHolder.itemView.findViewById(R.id.slice_view); + final ViewFlipper viewFlipper = viewHolder.itemView.findViewById(R.id.viewFlipper); + mRenderer.bindView(viewHolder, buildContextualCard(TEST_SLICE_URI)); + + card.performLongClick(); + mRenderer.onStop(); + + assertThat(viewFlipper.getCurrentView()).isInstanceOf(SliceView.class); + } + private RecyclerView.ViewHolder getSliceViewHolder() { final int viewType = mRenderer.getViewType(false /* isHalfWidth */); final RecyclerView recyclerView = new RecyclerView(mActivity); @@ -169,6 +216,7 @@ public class SliceContextualCardRendererTest { private ContextualCard buildContextualCard(String sliceUri) { return new ContextualCard.Builder() .setName("test_name") + .setCardType(ContextualCard.CardType.SLICE) .setSliceUri(Uri.parse(sliceUri)) .build(); } From ca2291cc309d946c62f832b770e7b9a2db9d4e95 Mon Sep 17 00:00:00 2001 From: Eran Messeri Date: Tue, 4 Dec 2018 12:31:05 +0000 Subject: [PATCH 3/3] Fix manual certificate installation Fix a bug where, when the user manually imports a certificate + key into KeyChain, the imported certificate would sometimes not be marked as user-selectable and so the user would not see it when KeyChaine.choosePrivateKeyAlias was called. The cause was the async task for marking the key as user-selectable sometimes executing after the activity for installing the certificate has terminated, leading it to try and interact with an already-terminated KeyChain service. The fix is to call the activity's finish() method only after the key is marked as user-selectable. Test: Manual, using CtsVerifier KeyChain Storage test. Bug: 116716944 Change-Id: I23832ff7b609d7c609e734af48e9a0642f1df527 --- .../android/settings/CredentialStorage.java | 32 +++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/src/com/android/settings/CredentialStorage.java b/src/com/android/settings/CredentialStorage.java index 7b0be943bc5..5ab543f65d9 100644 --- a/src/com/android/settings/CredentialStorage.java +++ b/src/com/android/settings/CredentialStorage.java @@ -172,8 +172,9 @@ public final class CredentialStorage extends FragmentActivity { dialog.show(getSupportFragmentManager(), ConfigureKeyGuardDialog.TAG); return; } - installIfAvailable(); - finish(); + if (installIfAvailable()) { + finish(); + } return; } } @@ -217,10 +218,13 @@ public final class CredentialStorage extends FragmentActivity { /** * Install credentials if available, otherwise do nothing. + * + * @return true if the installation is done and the activity should be finished, false if + * an asynchronous task is pending and will finish the activity when it's done. */ - private void installIfAvailable() { + private boolean installIfAvailable() { if (mInstallBundle == null || mInstallBundle.isEmpty()) { - return; + return true; } final Bundle bundle = mInstallBundle; @@ -235,16 +239,17 @@ public final class CredentialStorage extends FragmentActivity { if (uid != Process.WIFI_UID) { Log.e(TAG, "Failed to install credentials as uid " + uid + ": cross-user installs" + " may only target wifi uids"); - return; + return true; } final Intent installIntent = new Intent(ACTION_INSTALL) .setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT) .putExtras(bundle); startActivityAsUser(installIntent, new UserHandle(dstUserId)); - return; + return true; } + boolean shouldFinish = true; if (bundle.containsKey(Credentials.EXTRA_USER_PRIVATE_KEY_NAME)) { final String key = bundle.getString(Credentials.EXTRA_USER_PRIVATE_KEY_NAME); final byte[] value = bundle.getByteArray(Credentials.EXTRA_USER_PRIVATE_KEY_DATA); @@ -259,7 +264,7 @@ public final class CredentialStorage extends FragmentActivity { if (!mKeyStore.importKey(key, value, uid, flags)) { Log.e(TAG, "Failed to install " + key + " as uid " + uid); - return; + return true; } // The key was prepended USER_PRIVATE_KEY by the CredentialHelper. However, // KeyChain internally uses the raw alias name and only prepends USER_PRIVATE_KEY @@ -270,6 +275,7 @@ public final class CredentialStorage extends FragmentActivity { if (uid == Process.SYSTEM_UID || uid == KeyStore.UID_SELF) { new MarkKeyAsUserSelectable( key.replaceFirst("^" + Credentials.USER_PRIVATE_KEY, "")).execute(); + shouldFinish = false; } } @@ -281,7 +287,7 @@ public final class CredentialStorage extends FragmentActivity { if (!mKeyStore.put(certName, certData, uid, flags)) { Log.e(TAG, "Failed to install " + certName + " as uid " + uid); - return; + return shouldFinish; } } @@ -291,7 +297,7 @@ public final class CredentialStorage extends FragmentActivity { if (!mKeyStore.put(caListName, caListData, uid, flags)) { Log.e(TAG, "Failed to install " + caListName + " as uid " + uid); - return; + return shouldFinish; } } @@ -300,6 +306,7 @@ public final class CredentialStorage extends FragmentActivity { sendBroadcast(broadcast); setResult(RESULT_OK); + return shouldFinish; } /** @@ -411,6 +418,13 @@ public final class CredentialStorage extends FragmentActivity { return false; } } + + @Override + protected void onPostExecute(Boolean result) { + Log.i(TAG, String.format("Marked alias %s as selectable, success? %s", + mAlias, result)); + CredentialStorage.this.finish(); + } } /**