In my device i already set a password of device, now i install my app which is managed by device policy manager. now when i call this method
int currentPolicy = devicePolicyManager.getPasswordQuality(demoDeviceAdmin);
if(currentPolicy==262144)passwordType="Alphabetic";
else if(currentPolicy==327680)passwordType="Alphanumeric";
else if(currentPolicy==131072)passwordType="Numeric";
//if(currentPolicy==196608)passwordType="PASSWORD_QUALITY_NUMERIC_COMPLEX";
else if(currentPolicy==393216)passwordType="Complex";
else if(currentPolicy==196608)passwordType="Pattern";
else if(currentPolicy==0)passwordType="None";
it gives me password type none. Now if i set password through device policy manager in my application like this
Intent intent = new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
Then now if again get password quality it gives me correct value.
in First time i think device policy manager don't have or store old password type.
So my question is how to get password quality before setting password from my application through device policy manager.
Thank You
getPasswordQuality doesn't report the quality on the current password, it only reports the policy setting of lowest allowed password quality.
Basically getPasswordQuality returns the value set using the setPasswordQuality of the admin or the aggregate value if several admins are active.
The admin can check if the current password is good enough by calling DevicePolicyManager.isActivePasswordSufficient().
Keyguard reports back the current password status to DevicePolicyManager using the hidden method setActivePasswordState.
To my knowledge, this info is not available to a third party admin app.
If an incorrect password is entered, keyguard calls DevicePolicyManager.reportFailedPasswordAttempt which initiates a factory reset if too many failed attempts have been made.
Hope this helps.
/Marek Pola (Employee of Sony Mobile)
For devices before Android 6 (exclusive), you can get the unlocking method type by calling a hidden function getActivePasswordQuality() in class LockPatternUtils (you can call the function via Java reflection). But for Android 6 and above, I have not figured out how to get the unlocking method type.
/**
* Determine authentication method type (DAS, PIN, Password or Biometric)
*/
private String getAuthenticationMethodType(){
String LOCK_PATTERN_UTILS="com.android.internal.widget.LockPatternUtils";
String ACTIVE_PASSWORD_QUALITY="getActivePasswordQuality";
int lockProtectionLevel=0;
try{
Class <?> lockPatternUtilsClass=Class.forName(LOCK_PATTERN_UTILS);
Object lockPatternUtils=lockPatternUtilsClass.getConstructor(Context.class).newInstance(this);
if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.M){
// Have not figured out how to get the unlocking method type for Android 6 and above devices. The same method will not work on Android 6. See detailed explanation below.
}else {
Method method = lockPatternUtilsClass.getMethod(ACTIVE_PASSWORD_QUALITY);
lockProtectionLevel=Integer.valueOf(String.valueOf(method.invoke(lockPatternUtils,null)));
}
}catch (Exception e){
e.printStackTrace();
}
switch (lockProtectionLevel){
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
return "PIN";
case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
return "Password";
case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
return "DAS";
case DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK:
return "Biometric";
default:return "None";
}
}
For Android 6, after all my tests so far, I found that if we still want to get the unlocking method with function getActivePasswordQuality(int userId), the app must have permission android.permission.ACCESS_KEYGUARD_SECURE_STORAGE in order to read lockscreen.password_type, and get the unlocking method. However, the permission ACCESS_KEYGUARD_SECURE_STORAGE is a signature permission, which means we need to sign the app with the same key that the manufacturers use to sign the system (or to say the permission), so as to grant the signature permission to the app successfully.
Hope this helps.
Related
As we all know, from android 9.0, android introduced BiometricPrompt Api to provide standard authentication experience across growing range of biometric sensors (E.g Fingerprint,Face ID etc).
Now with this new BiometricPrompt Api user can get authenticated via fingerprint, face scanner or iris scanned (depend on their biometric preference). BiometricPrompt api will take care of this and it will notify us via various callbacks.
Below is my code to display Biometric Prompt.
biometricPrompt = new BiometricPrompt.Builder(context)
.setTitle("FingerPrint Authentication")
.setSubtitle("Login via Fingerprint")
.setDescription("Touch Fingerprint Sensor")
.setNegativeButton("Cancel", context.getMainExecutor(),
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
Log.d(TAG,"Cancelled");
}
})
.build();
Now if you see my code, i am setting title as a Fingerprint Authentication. Now in device setting, if user had set a Biometric Preference as a Face ID instead of FingerPrint then this biometricPrompt will authenticate user via faceID and fingerprint sensor wont work even if user keep touching sensor. This would create confusion as Biometric title is saying that "Fingerprint authentication" and user is actually getting authenticated via faceID
Is there any way by which we can know what Biometric preference user has selected (e.g Fingerprint or FaceID)? So based upon that preference i can show appropriate message on BiometricPrompt so user wont get confused.
I already explored all api from BiometricPrompt but could find anything related to BiometricPreference.
Any help would be highly appreciated.
While not a perfect solution, you can use the PackageManager API to determine whether a device has the authenticator hardware, e.g.:
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE))
{
}
I've created a helper class as follows:
class BiometricAuthenticator
{
public enum BiometricType
{
FINGERPRINT,
FACE,
IRIS,
NONE
}
public static boolean hasBiometricAuthenticator(Context context)
{
int biometry = BiometricManager.BIOMETRIC_ERROR_UNSUPPORTED;
if (VERSION.SDK_INT >= 30)
biometry = BiometricManager.from(context).canAuthenticate(Authenticators.BIOMETRIC_STRONG | Authenticators.BIOMETRIC_WEAK);
else
biometry = BiometricManager.from(context).canAuthenticate();
switch (biometry)
{
case BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE:
case BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED:
case BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE:
return (false);
case BiometricManager.BIOMETRIC_SUCCESS:
return true;
}
return (false);
}
/**
* biometricType()
*
* returns type of biometry supported
*/
public static BiometricType biometricType(Context context)
{
if (VERSION.SDK_INT < 23)
return BiometricType.NONE;
PackageManager packageManager = context.getPackageManager();
// SDK 29 adds FACE and IRIS authentication
if (VERSION.SDK_INT >= 29)
{
if (packageManager.hasSystemFeature(PackageManager.FEATURE_FACE))
return BiometricType.FACE;
if (packageManager.hasSystemFeature(PackageManager.FEATURE_IRIS))
return BiometricType.IRIS;
if (packageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT))
return BiometricType.FINGERPRINT;
return BiometricType.NONE;
}
// SDK 23-28 offer FINGERPRINT only
return (packageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT) ? BiometricType.FINGERPRINT : BiometricType.NONE);
}
}
This allows you to determine if a biometric authenticator is present (hasBiometricAuthenticator), and if so, return the type of authenticator as a BiometricType enum.
A device could theoretically have multiple authenticators, and biometricType() will return FACE, IRIS, then FINGERPRINT in that order of preference on API30+ devices.
Hopefully Google will expose better API in the future, but these tricks will at least help get appropriate prompts on the dialog
There is no mean of knowing this type of information for now, an issue had been opened last year to ask for it (https://issuetracker.google.com/issues/111315641). As Android tried to simplify the path for developer to implement authentication in their apps, there is a lack of options in the BiometricPrompt implementation (see the Android document for BiometricPrompt implementation).
In your case you can simply change your title String to "Biometric Authentication", and so with the other Strings. For an example see the blog posts pointed to below.
Your code might look as follows. But I'd also recommend you use the strings.xml resource file instead of hard-coding your Strings in the code. For example, in the future, you may want translation services.
biometricPrompt = new BiometricPrompt.Builder(context)
.setTitle("Biometric Authentication")
.setSubtitle("Login via biometrics")
.setDescription("Use the Biometrics Sensor")
.setNegativeButton("Cancel", context.getMainExecutor(),
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
Log.d(TAG,"Cancelled");
}
})
.build();
More broadly, the privacy implications must be evaluated before the API team makes a decision whether developers should know a user's biometric preferences. It's really not clear why a developer should need this information. Two blog posts have been published that touch on the subject of Strong vs Weak biometrics and how to implement them (blog one, blog two). Beyond that distinction (i.e. Strong vs Weak), what form-factors a user prefers or ends up using doesn't seem pertinent.
In Android R was added a method called setAllowedAuthenticators
public BiometricPrompt.Builder setAllowedAuthenticators (int authenticators)
Optional: Specifies the type(s) of authenticators that may be invoked by BiometricPrompt to authenticate the user. Available authenticator types are defined in Authenticators and can be combined via bitwise OR. Defaults to:
Authenticators#BIOMETRIC_WEAK for non-crypto authentication, or
Authenticators#BIOMETRIC_STRONG for crypto-based authentication.
If this method is used and no authenticator of any of the specified types is available at the time BiometricPrompt#authenticate(...) is called, authentication will be canceled and AuthenticationCallback#onAuthenticationError(int, CharSequence) will be invoked with an appropriate error code.
This method should be preferred over setDeviceCredentialAllowed(boolean) and overrides the latter if both are used. Using this method to enable device credential authentication (with Authenticators#DEVICE_CREDENTIAL) will replace the negative button on the prompt, making it an error to also call setNegativeButton(java.lang.CharSequence, java.util.concurrent.Executor, android.content.DialogInterface.OnClickListener).
authenticators
A bit field representing all valid authenticator types that may be
invoked by the prompt. Value is either 0 or a combination of
BiometricManager.Authenticators.BIOMETRIC_STRONG,
BiometricManager.Authenticators.BIOMETRIC_WEAK, and
BiometricManager.Authenticators.DEVICE_CREDENTIAL
I've got an app which connect itself programatically to a wifi connection. My problem is, I want to handle the case, that the password is wrong. I want to detect that the password is not correct in runtime. To be precise I've got a progressdialog running while the connection is established, so if the password is wrong the progressdialog is just shown all the time and can't be skipped. A further note: I handled a password which is less than 8 characters by using this code:
if(!m_wifiManager.enableNetwork(netId, true)) {
progressDialogConnecting.dismiss();
createInfoMessageDialog(CONST.WIFI_CON_FAILED_TITLE, CONST.WIFI_CON_FAILED_MSG_CONFAILURE);
m_wifiManager.reconnect();
return;
}
If the key for the wifi connection is less than 8 characters, this if-case gets triggered. But if it is longer than 8 characters and wrong I get an endless state of showing the progress dialog.
What I exactly want to ask: how do I handle 1. wrong password 2. connection states (just like Android system showing me the toasts "Connected to Wifi xyz") ? AND is it even possible to handel the first one (wrong password)?
Here is the code, that did not work for handling connection established event (this is just the wifirecevier, I also registered it in the activity):
public class WifiReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (action.equals(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION)) {
if (intent.getBooleanExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, false)){
if(wrongNetworkConnected)
progressDialogConnecting.dismiss();
}
}
} else {
}
}
}
}
Edit: What I am currently doing, is that I have a Handler which tells me to whom I am connected. That's useful because I can say that after the reconnect() I am reconnected to the old network (current network) and not the new one - so apparently the password could be wrong (or something else), because I could not connect to the new network.
The problem about this method is that first of all it takes too much time and secondly it is not reliable. I can lie and say that if you will get reconnected to your current network it is the fault of a wrong password, but actually it is not 100% sure that you cannot reconnect because of this - it may also have other reasons. So I am still searching for a simple feedback/handle from the suplicant that the password is wrong, just like the android api does in the wifi settings of each android device...
My problem is, I want to handle the case, that the password is wrong.
After some research I found this post which is not marked as answered but it still worked for me very well.
Here is the if-case in which the program jumps (already tested several times by me) if there is an authentication error --> e.g. wrong password:
int supl_error=intent.getIntExtra(WifiManager.EXTRA_SUPPLICANT_ERROR, -1);
if(supl_error==WifiManager.ERROR_AUTHENTICATING){
// DO SOMETHING
}
NOTE: As seen in the linked post above this if-case should appear in a BroadcastReceiver adding the intent WifiManager.SUPPLICANT_STATE_CHANGED_ACTIONto the receiver-registration in your activity-class.
I am working on a small android automation assignment. I am writing automation code to test the initial device start up process (When you take out device out of box and switch on for first time).
One of the sates is when we have to enter Google account credentials for which email and password fields are encompassed in android.webkit.WebView class. I am not able to access email and password field using UiAutomatorview. Being unable to access, hence I can't enter actual inputs in those fields. Also since this is just kind of a booting up a device, can't run any other app that will help me identify UI components.
Did anyone faced similar problem and able to overcome it ?
I tried following way to locate the object BUT that also does not return any UI object
UiObject2 googleAccName = mUiDevice.findObject(By.text("Enter your email"));
if( googleAccName != null &&
googleAccName.isEnabled() &&
googleAccName.isClickable()) {
googleAccName.click();
googleAccName.setText("xxxxx#gmail.com");
}
else {
Log.e(TAG, "No Text field to enter email found !!");
Assert.fail("failed to setup google account");
}
UiAutomator doesn't work with Web Views. If it is a webview (as you say in your title) it's not possible to do it using UiAutomator.
On JellyBean device.
I'm following this to request an oauth2 token, e.g.
AccountManager am = AccountManager.get(getActivity());
am.invalidateAuthToken(MY_AUTH_TOKEN_TYPE, null);
am.getAuthToken(aGoogleAccount, MY_AUTH_TOKEN_TYPE, null, this,
new OnTokenAcquired(), new Handler(new OnError()));
and then make the check as per the later code sample:
private class OnTokenAcquired implements AccountManagerCallback<Bundle> {
#Override
public void run(AccountManagerFuture<Bundle> result) {
Bundle bundle = result.getResult();
...
Intent launch = (Intent) bundle.get(AccountManager.KEY_INTENT);
if (launch != null) {
startActivityForResult(launch, 0);
return;
}
}
}
I never get a KEY_INTENT. I understand the following:
There may be many reasons for the authenticator to return an Intent. It may be the first time the user has logged in to this account. Perhaps the user's account has expired and they need to log in again, or perhaps their stored credentials are incorrect. Maybe the account requires two-factor authentication or it needs to activate the camera to do a retina scan. It doesn't really matter what the reason is. If you want a valid token, you're going to have to fire off the Intent to get it.
However, the getAuthToken always results in the permission screen, or login screen, appearing before the code hits the run method at which point the token is valid. I've tried:
Turning on 2 step authentication. Account login is requested before run so always have the token in run.
Changing the password on the server. Again account login is requested before run so always have the token in run.
Don't have the ability to try a retina scan so somewhat at a loss.
EDIT 1 The problem I have is that there may be a circumstance where I will get a KEY_INTENT and so I'd rather test this code path now rather when when it's out in the field.
Thanks in advance.
Peter.
Had a chance to do something similar on a project. This not the exactly the same as your code, and I still say that the callback docs have too many 'maybes' to be certain of how it should work, but if you use this method passing false for notifyAuthFailure, you will get an intent with the re-login screen if you change the password or enable 2FA. This is for ClientLogin, but should work similarly for OAuth 2 (not tested though). Something like:
// using Calendar ClientLogin for simplicity
Bundle authResult = am.getAuthToken(account, "cl", false, null, null).getResult();
if (authResult.containsKey(AccountManager.KEY_INTENT)) {
Intent authIntent = authResult.getParcelable(AccountManager.KEY_INTENT);
// start activity or show notification
}
I think you need to call getResult(), like this:
Intent launch = (Intent)result.getResult().get(AccountManager.KEY_INTENT);
You're using the version of getAuthToken which uses an Activity to invoke the access authorization prompt. That version of getAuthToken does not return an intent since the supplied activity is used to launch the corresponding intent. If you want to manually launch an intent, use the version of getAuthToken that was deprecated in API level 14. See the following for more information:
http://developer.android.com/reference/android/accounts/AccountManager.html#getAuthToken%28android.accounts.Account,%20java.lang.String,%20boolean,%20android.accounts.AccountManagerCallback%3Candroid.os.Bundle%3E,%20android.os.Handler%29
My requirement is, First time application is installed in the Android phone, Need to get the licence code from particular shop/organization. I have created generation key using phone model number.nOW THE PROBLEM IS If its first only need to show license screen otherwise go to first screen. How we can identify the particular app is installed already or not . / from the registry ? In here registry is available.
I couldn't explore my very deeply or clearly. Sorry for that.
Please help me.
Thanks in advance...
You could always set a bool value in the android.content.SharedPreferences, then in the first oncreate() check to see whether that bool value is false.
If it is push the license screen intent and perform a check for application, if its there update the preference to true. So on next start it will skip over it, where you can load your main screen.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
settings = getSharedPreferences(PREFS_NAME, 0);
boolean enteredDetails = settings.getBoolean("FirstTime", false);
if(enteredDetails){
setContentView(R.layout.main); //loads the main screen
}
else{
startActivityForResult(new Intent(this, License.class), GET_DETAILS);
}
}
You can simply use Context.openFileInput() / Context.openFileOutput() to store a piece of information that will tell your app whether the license screen was already shown. This way you can use something like this in your main Activity's onCreate():
if (nothingWrittenInAFileCalled(FILE_NAME)) { // using Context.openFileInput()
showLicense();
writeAFileCalled(FILE_NAME); // using Context.openFileOutput()
}
If this is not satisfactory, this is also something you can check on license server side. If you send the license server a hash of the IMEI, as an example, your license server will be able to determine whether the app was already installed or not. In that case, prefer a non reversible hash: this is to avoid sending/storing the IMEI as one can see this piece of information as private data.