Files
app_Settings/src/com/android/settings/TrustedCredentialsDialogBuilder.java
Victor Chang f2e02db61e Disable trust button when no screenlock is set
Remove button is still allowed as it has been allowed in MNC
(Trust button is new in NYC)

Bug: 28161447
Change-Id: I8df73e1008c2e805f4d0b2ec68d8a4aa3bf75aba
2016-04-21 16:14:09 +01:00

361 lines
14 KiB
Java

/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings;
import android.annotation.NonNull;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.admin.DevicePolicyManager;
import android.content.DialogInterface;
import android.content.pm.UserInfo;
import android.net.http.SslCertificate;
import android.os.UserHandle;
import android.os.UserManager;
import android.view.View;
import android.view.animation.AnimationUtils;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Spinner;
import com.android.internal.widget.LockPatternUtils;
import com.android.settings.TrustedCredentialsSettings.CertHolder;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
class TrustedCredentialsDialogBuilder extends AlertDialog.Builder {
public interface DelegateInterface {
List<X509Certificate> getX509CertsFromCertHolder(CertHolder certHolder);
void removeOrInstallCert(CertHolder certHolder);
}
private final DialogEventHandler mDialogEventHandler;
public TrustedCredentialsDialogBuilder(Activity activity, DelegateInterface delegate) {
super(activity);
mDialogEventHandler = new DialogEventHandler(activity, delegate);
initDefaultBuilderParams();
}
public TrustedCredentialsDialogBuilder setCertHolder(CertHolder certHolder) {
return setCertHolders(certHolder == null ? new CertHolder[0]
: new CertHolder[]{certHolder});
}
public TrustedCredentialsDialogBuilder setCertHolders(@NonNull CertHolder[] certHolders) {
mDialogEventHandler.setCertHolders(certHolders);
return this;
}
@Override
public AlertDialog create() {
AlertDialog dialog = super.create();
dialog.setOnShowListener(mDialogEventHandler);
mDialogEventHandler.setDialog(dialog);
return dialog;
}
private void initDefaultBuilderParams() {
setTitle(com.android.internal.R.string.ssl_certificate);
setView(mDialogEventHandler.mRootContainer);
// Enable buttons here. The actual labels and listeners are configured in nextOrDismiss
setPositiveButton(R.string.trusted_credentials_trust_label, null);
setNegativeButton(android.R.string.ok, null);
}
private static class DialogEventHandler implements DialogInterface.OnShowListener,
View.OnClickListener {
private static final long OUT_DURATION_MS = 300;
private static final long IN_DURATION_MS = 200;
private final Activity mActivity;
private final DevicePolicyManager mDpm;
private final UserManager mUserManager;
private final DelegateInterface mDelegate;
private final LinearLayout mRootContainer;
private int mCurrentCertIndex = -1;
private AlertDialog mDialog;
private Button mPositiveButton;
private Button mNegativeButton;
private boolean mNeedsApproval;
private CertHolder[] mCertHolders = new CertHolder[0];
private View mCurrentCertLayout = null;
public DialogEventHandler(Activity activity, DelegateInterface delegate) {
mActivity = activity;
mDpm = activity.getSystemService(DevicePolicyManager.class);
mUserManager = activity.getSystemService(UserManager.class);
mDelegate = delegate;
mRootContainer = new LinearLayout(mActivity);
mRootContainer.setOrientation(LinearLayout.VERTICAL);
}
public void setDialog(AlertDialog dialog) {
mDialog = dialog;
}
public void setCertHolders(CertHolder[] certHolder) {
mCertHolders = certHolder;
}
@Override
public void onShow(DialogInterface dialogInterface) {
// Config the display content only when the dialog is shown because the
// positive/negative buttons don't exist until the dialog is shown
nextOrDismiss();
}
@Override
public void onClick(View view) {
if (view == mPositiveButton) {
if (mNeedsApproval) {
onClickTrust();
} else {
onClickOk();
}
} else if (view == mNegativeButton) {
onClickRemove();
}
}
private void onClickOk() {
nextOrDismiss();
}
private void onClickTrust() {
CertHolder certHolder = getCurrentCertInfo();
mDpm.approveCaCert(certHolder.getAlias(), certHolder.getUserId(), true);
nextOrDismiss();
}
private void onClickRemove() {
final CertHolder certHolder = getCurrentCertInfo();
new AlertDialog.Builder(mActivity)
.setMessage(getButtonConfirmation(certHolder))
.setPositiveButton(android.R.string.yes,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
mDelegate.removeOrInstallCert(certHolder);
dialog.dismiss();
nextOrDismiss();
}
})
.setNegativeButton(android.R.string.no, null)
.show();
}
private CertHolder getCurrentCertInfo() {
return mCurrentCertIndex < mCertHolders.length ? mCertHolders[mCurrentCertIndex] : null;
}
private void nextOrDismiss() {
mCurrentCertIndex++;
// find next non-null cert or dismiss
while (mCurrentCertIndex < mCertHolders.length && getCurrentCertInfo() == null) {
mCurrentCertIndex++;
}
if (mCurrentCertIndex >= mCertHolders.length) {
mDialog.dismiss();
return;
}
updateViewContainer();
updatePositiveButton();
updateNegativeButton();
}
/**
* @return true if current user or parent user is guarded by screenlock
*/
private boolean isUserSecure(int userId) {
final LockPatternUtils lockPatternUtils = new LockPatternUtils(mActivity);
if (lockPatternUtils.isSecure(userId)) {
return true;
}
UserInfo parentUser = mUserManager.getProfileParent(userId);
if (parentUser == null) {
return false;
}
return lockPatternUtils.isSecure(parentUser.id);
}
private void updatePositiveButton() {
final CertHolder certHolder = getCurrentCertInfo();
mNeedsApproval = !certHolder.isSystemCert()
&& isUserSecure(certHolder.getUserId())
&& !mDpm.isCaCertApproved(certHolder.getAlias(), certHolder.getUserId());
// The ok button is optional. User can still dismiss the dialog by other means.
// Display it only when trust button is not displayed, because we want users to
// either remove or trust a CA cert when the cert is installed by DPC app.
CharSequence displayText = mActivity.getText(mNeedsApproval
? R.string.trusted_credentials_trust_label
: android.R.string.ok);
mPositiveButton = updateButton(DialogInterface.BUTTON_POSITIVE, displayText);
}
private void updateNegativeButton() {
final CertHolder certHolder = getCurrentCertInfo();
final boolean showRemoveButton = !mUserManager.hasUserRestriction(
UserManager.DISALLOW_CONFIG_CREDENTIALS,
new UserHandle(certHolder.getUserId()));
CharSequence displayText = mActivity.getText(getButtonLabel(certHolder));
mNegativeButton = updateButton(DialogInterface.BUTTON_NEGATIVE, displayText);
mNegativeButton.setVisibility(showRemoveButton ? View.VISIBLE : View.GONE);
}
/**
* mDialog.setButton doesn't trigger text refresh since mDialog has been shown.
* It's invoked only in case mDialog is refreshed.
* setOnClickListener is invoked to avoid dismiss dialog onClick
*/
private Button updateButton(int buttonType, CharSequence displayText) {
mDialog.setButton(buttonType, displayText, (DialogInterface.OnClickListener) null);
Button button = mDialog.getButton(buttonType);
button.setText(displayText);
button.setOnClickListener(this);
return button;
}
private void updateViewContainer() {
CertHolder certHolder = getCurrentCertInfo();
LinearLayout nextCertLayout = getCertLayout(certHolder);
// Displaying first cert doesn't require animation
if (mCurrentCertLayout == null) {
mCurrentCertLayout = nextCertLayout;
mRootContainer.addView(mCurrentCertLayout);
} else {
animateViewTransition(nextCertLayout);
}
}
private LinearLayout getCertLayout(final CertHolder certHolder) {
final ArrayList<View> views = new ArrayList<View>();
final ArrayList<String> titles = new ArrayList<String>();
List<X509Certificate> certificates = mDelegate.getX509CertsFromCertHolder(certHolder);
if (certificates != null) {
for (X509Certificate certificate : certificates) {
SslCertificate sslCert = new SslCertificate(certificate);
views.add(sslCert.inflateCertificateView(mActivity));
titles.add(sslCert.getIssuedTo().getCName());
}
}
ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(mActivity,
android.R.layout.simple_spinner_item,
titles);
arrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
Spinner spinner = new Spinner(mActivity);
spinner.setAdapter(arrayAdapter);
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position,
long id) {
for (int i = 0; i < views.size(); i++) {
views.get(i).setVisibility(i == position ? View.VISIBLE : View.GONE);
}
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
LinearLayout certLayout = new LinearLayout(mActivity);
certLayout.setOrientation(LinearLayout.VERTICAL);
certLayout.addView(spinner);
for (int i = 0; i < views.size(); ++i) {
View certificateView = views.get(i);
// Show first cert by default
certificateView.setVisibility(i == 0 ? View.VISIBLE : View.GONE);
certLayout.addView(certificateView);
}
return certLayout;
}
private static int getButtonConfirmation(CertHolder certHolder) {
return certHolder.isSystemCert() ? ( certHolder.isDeleted()
? R.string.trusted_credentials_enable_confirmation
: R.string.trusted_credentials_disable_confirmation )
: R.string.trusted_credentials_remove_confirmation;
}
private static int getButtonLabel(CertHolder certHolder) {
return certHolder.isSystemCert() ? ( certHolder.isDeleted()
? R.string.trusted_credentials_enable_label
: R.string.trusted_credentials_disable_label )
: R.string.trusted_credentials_remove_label;
}
/* Animation code */
private void animateViewTransition(final View nextCertView) {
animateOldContent(new Runnable() {
@Override
public void run() {
addAndAnimateNewContent(nextCertView);
}
});
}
private void animateOldContent(Runnable callback) {
// Fade out
mCurrentCertLayout.animate()
.alpha(0)
.setDuration(OUT_DURATION_MS)
.setInterpolator(AnimationUtils.loadInterpolator(mActivity,
android.R.interpolator.fast_out_linear_in))
.withEndAction(callback)
.start();
}
private void addAndAnimateNewContent(View nextCertLayout) {
mCurrentCertLayout = nextCertLayout;
mRootContainer.removeAllViews();
mRootContainer.addView(nextCertLayout);
mRootContainer.addOnLayoutChangeListener( new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
mRootContainer.removeOnLayoutChangeListener(this);
// Animate slide in from the right
final int containerWidth = mRootContainer.getWidth();
mCurrentCertLayout.setTranslationX(containerWidth);
mCurrentCertLayout.animate()
.translationX(0)
.setInterpolator(AnimationUtils.loadInterpolator(mActivity,
android.R.interpolator.linear_out_slow_in))
.setDuration(IN_DURATION_MS)
.start();
}
});
}
}
}