Add request manage credentials to Settings
Background * This is part of the work to support a credential management app on unmanaged devices. Changes * Add new activity to Settings to display a screen to the user requesting whether the calling app can manage their KeyChain credentials. * Display the authentication policy Manual Testing * Verify screen is not displayed if intent action is not android.security.MANAGE_CREDENTIALS * Verify screen is not displayed if authentication policy is not valid * Verify button panel is visible if all items in the authentication policy are displayed * Verify button panel is not visible if not all items in the authentication policy are displayed. Verify that scrolling to the bottom of the item list, the button panel becomes visible. Bug: 165641221 Test: Manual testing make RunSettingsRoboTests -j ROBOTEST_FILTER=com.android.settings.security.RequestManageCredentialsTest Change-Id: Ie23b226f1a285b3de6ec3e91b8880d9144bb24a3
This commit is contained in:
172
src/com/android/settings/security/RequestManageCredentials.java
Normal file
172
src/com/android/settings/security/RequestManageCredentials.java
Normal file
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.security;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.app.Activity;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.os.Bundle;
|
||||
import android.security.AppUriAuthenticationPolicy;
|
||||
import android.security.Credentials;
|
||||
import android.security.KeyChain;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
/**
|
||||
* Displays a full screen to the user asking whether the calling app can manage the user's
|
||||
* KeyChain credentials. This screen includes the authentication policy highlighting what apps and
|
||||
* URLs the calling app can authenticate the user to.
|
||||
* <p>
|
||||
* Users can allow or deny the calling app. If denied, the calling app may re-request this
|
||||
* capability. If allowed, the calling app will become the credential management app and will be
|
||||
* able to manage the user's KeyChain credentials. The following APIs can be called to manage
|
||||
* KeyChain credentials:
|
||||
* {@link DevicePolicyManager#installKeyPair}
|
||||
* {@link DevicePolicyManager#removeKeyPair}
|
||||
* {@link DevicePolicyManager#generateKeyPair}
|
||||
* {@link DevicePolicyManager#setKeyPairCertificate}
|
||||
* <p>
|
||||
*
|
||||
* @see AppUriAuthenticationPolicy
|
||||
*/
|
||||
public class RequestManageCredentials extends Activity {
|
||||
|
||||
private static final String TAG = "ManageCredentials";
|
||||
|
||||
private String mCredentialManagerPackage;
|
||||
private AppUriAuthenticationPolicy mAuthenticationPolicy;
|
||||
|
||||
private RecyclerView mRecyclerView;
|
||||
private LinearLayoutManager mLayoutManager;
|
||||
private LinearLayout mButtonPanel;
|
||||
|
||||
private boolean mDisplayingButtonPanel = false;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (Credentials.ACTION_MANAGE_CREDENTIALS.equals(getIntent().getAction())) {
|
||||
setContentView(R.layout.request_manage_credentials);
|
||||
// This is not authenticated, as any app can ask to be the credential management app.
|
||||
mCredentialManagerPackage = getReferrer().getHost();
|
||||
mAuthenticationPolicy =
|
||||
getIntent().getParcelableExtra(KeyChain.EXTRA_AUTHENTICATION_POLICY);
|
||||
enforceValidAuthenticationPolicy(mAuthenticationPolicy);
|
||||
|
||||
loadRecyclerView();
|
||||
loadButtons();
|
||||
addOnScrollListener();
|
||||
} else {
|
||||
Log.e(TAG, "Unable to start activity because intent action is not "
|
||||
+ Credentials.ACTION_MANAGE_CREDENTIALS);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
private void loadRecyclerView() {
|
||||
mLayoutManager = new LinearLayoutManager(this);
|
||||
mRecyclerView = findViewById(R.id.apps_list);
|
||||
mRecyclerView.setLayoutManager(mLayoutManager);
|
||||
|
||||
CredentialManagementAppAdapter recyclerViewAdapter = new CredentialManagementAppAdapter(
|
||||
this, mCredentialManagerPackage, mAuthenticationPolicy.getAppAndUriMappings());
|
||||
mRecyclerView.setAdapter(recyclerViewAdapter);
|
||||
}
|
||||
|
||||
private void loadButtons() {
|
||||
mButtonPanel = findViewById(R.id.button_panel);
|
||||
Button dontAllowButton = findViewById(R.id.dont_allow_button);
|
||||
Button allowButton = findViewById(R.id.allow_button);
|
||||
|
||||
dontAllowButton.setOnClickListener(finishRequestManageCredentials());
|
||||
allowButton.setOnClickListener(setCredentialManagementApp());
|
||||
}
|
||||
|
||||
private View.OnClickListener finishRequestManageCredentials() {
|
||||
return v -> {
|
||||
Toast.makeText(this, R.string.request_manage_credentials_dont_allow,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
setResult(RESULT_CANCELED);
|
||||
finish();
|
||||
};
|
||||
}
|
||||
|
||||
private View.OnClickListener setCredentialManagementApp() {
|
||||
return v -> {
|
||||
// TODO: Implement allow logic
|
||||
Toast.makeText(this, R.string.request_manage_credentials_allow,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
finish();
|
||||
};
|
||||
}
|
||||
|
||||
private void addOnScrollListener() {
|
||||
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||
@Override
|
||||
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
|
||||
super.onScrolled(recyclerView, dx, dy);
|
||||
if (!mDisplayingButtonPanel) {
|
||||
if (isRecyclerScrollable()) {
|
||||
hideButtonPanel();
|
||||
} else {
|
||||
showButtonPanel();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void showButtonPanel() {
|
||||
// Add padding to remove overlap between recycler view and button panel.
|
||||
int padding_in_px = (int) (60 * getResources().getDisplayMetrics().density + 0.5f);
|
||||
mRecyclerView.setPadding(0, 0, 0, padding_in_px);
|
||||
mButtonPanel.setVisibility(View.VISIBLE);
|
||||
mDisplayingButtonPanel = true;
|
||||
}
|
||||
|
||||
private void hideButtonPanel() {
|
||||
mRecyclerView.setPadding(0, 0, 0, 0);
|
||||
mButtonPanel.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
private boolean isRecyclerScrollable() {
|
||||
if (mLayoutManager == null || mRecyclerView.getAdapter() == null) {
|
||||
return false;
|
||||
}
|
||||
return mLayoutManager.findLastCompletelyVisibleItemPosition()
|
||||
< mRecyclerView.getAdapter().getItemCount() - 1;
|
||||
}
|
||||
|
||||
private void enforceValidAuthenticationPolicy(AppUriAuthenticationPolicy policy) {
|
||||
// TODO: Check whether any of the aliases in the policy already exist
|
||||
if (policy == null || policy.getAppAndUriMappings().isEmpty()) {
|
||||
Log.e(TAG, "Invalid authentication policy");
|
||||
setResult(RESULT_CANCELED);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user