Home | History | Annotate | Download | only in app
      1 /*
      2  * Copyright (C) 2007 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.app;
     18 
     19 import android.Manifest;
     20 import android.annotation.NonNull;
     21 import android.annotation.Nullable;
     22 import android.annotation.RequiresPermission;
     23 import android.annotation.SystemApi;
     24 import android.annotation.SystemService;
     25 import android.app.trust.ITrustManager;
     26 import android.content.Context;
     27 import android.content.Intent;
     28 import android.content.pm.PackageManager;
     29 import android.content.pm.ResolveInfo;
     30 import android.os.Binder;
     31 import android.os.Handler;
     32 import android.os.IBinder;
     33 import android.os.RemoteException;
     34 import android.os.ServiceManager;
     35 import android.os.ServiceManager.ServiceNotFoundException;
     36 import android.os.UserHandle;
     37 import android.provider.Settings;
     38 import android.service.persistentdata.IPersistentDataBlockService;
     39 import android.util.Log;
     40 import android.view.IOnKeyguardExitResult;
     41 import android.view.IWindowManager;
     42 import android.view.WindowManager.LayoutParams;
     43 import android.view.WindowManagerGlobal;
     44 
     45 import com.android.internal.policy.IKeyguardDismissCallback;
     46 import com.android.internal.widget.LockPatternUtils;
     47 
     48 import java.util.List;
     49 
     50 /**
     51  * Class that can be used to lock and unlock the keyboard. The
     52  * actual class to control the keyboard locking is
     53  * {@link android.app.KeyguardManager.KeyguardLock}.
     54  */
     55 @SystemService(Context.KEYGUARD_SERVICE)
     56 public class KeyguardManager {
     57 
     58     private static final String TAG = "KeyguardManager";
     59 
     60     private final Context mContext;
     61     private final IWindowManager mWM;
     62     private final IActivityManager mAm;
     63     private final ITrustManager mTrustManager;
     64 
     65     /**
     66      * Intent used to prompt user for device credentials.
     67      * @hide
     68      */
     69     public static final String ACTION_CONFIRM_DEVICE_CREDENTIAL =
     70             "android.app.action.CONFIRM_DEVICE_CREDENTIAL";
     71 
     72     /**
     73      * Intent used to prompt user for device credentials.
     74      * @hide
     75      */
     76     public static final String ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER =
     77             "android.app.action.CONFIRM_DEVICE_CREDENTIAL_WITH_USER";
     78 
     79     /**
     80      * Intent used to prompt user for factory reset credentials.
     81      * @hide
     82      */
     83     public static final String ACTION_CONFIRM_FRP_CREDENTIAL =
     84             "android.app.action.CONFIRM_FRP_CREDENTIAL";
     85 
     86     /**
     87      * A CharSequence dialog title to show to the user when used with a
     88      * {@link #ACTION_CONFIRM_DEVICE_CREDENTIAL}.
     89      * @hide
     90      */
     91     public static final String EXTRA_TITLE = "android.app.extra.TITLE";
     92 
     93     /**
     94      * A CharSequence description to show to the user when used with
     95      * {@link #ACTION_CONFIRM_DEVICE_CREDENTIAL}.
     96      * @hide
     97      */
     98     public static final String EXTRA_DESCRIPTION = "android.app.extra.DESCRIPTION";
     99 
    100     /**
    101      * A CharSequence description to show to the user on the alternate button when used with
    102      * {@link #ACTION_CONFIRM_FRP_CREDENTIAL}.
    103      * @hide
    104      */
    105     public static final String EXTRA_ALTERNATE_BUTTON_LABEL =
    106             "android.app.extra.ALTERNATE_BUTTON_LABEL";
    107 
    108     /**
    109      * Result code returned by the activity started by
    110      * {@link #createConfirmFactoryResetCredentialIntent} indicating that the user clicked the
    111      * alternate button.
    112      *
    113      * @hide
    114      */
    115     public static final int RESULT_ALTERNATE = 1;
    116 
    117     /**
    118      * Get an intent to prompt the user to confirm credentials (pin, pattern or password)
    119      * for the current user of the device. The caller is expected to launch this activity using
    120      * {@link android.app.Activity#startActivityForResult(Intent, int)} and check for
    121      * {@link android.app.Activity#RESULT_OK} if the user successfully completes the challenge.
    122      *
    123      * @return the intent for launching the activity or null if no password is required.
    124      **/
    125     public Intent createConfirmDeviceCredentialIntent(CharSequence title, CharSequence description) {
    126         if (!isDeviceSecure()) return null;
    127         Intent intent = new Intent(ACTION_CONFIRM_DEVICE_CREDENTIAL);
    128         intent.putExtra(EXTRA_TITLE, title);
    129         intent.putExtra(EXTRA_DESCRIPTION, description);
    130 
    131         // explicitly set the package for security
    132         intent.setPackage(getSettingsPackageForIntent(intent));
    133         return intent;
    134     }
    135 
    136     /**
    137      * Get an intent to prompt the user to confirm credentials (pin, pattern or password)
    138      * for the given user. The caller is expected to launch this activity using
    139      * {@link android.app.Activity#startActivityForResult(Intent, int)} and check for
    140      * {@link android.app.Activity#RESULT_OK} if the user successfully completes the challenge.
    141      *
    142      * @return the intent for launching the activity or null if no password is required.
    143      *
    144      * @hide
    145      */
    146     public Intent createConfirmDeviceCredentialIntent(
    147             CharSequence title, CharSequence description, int userId) {
    148         if (!isDeviceSecure(userId)) return null;
    149         Intent intent = new Intent(ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER);
    150         intent.putExtra(EXTRA_TITLE, title);
    151         intent.putExtra(EXTRA_DESCRIPTION, description);
    152         intent.putExtra(Intent.EXTRA_USER_ID, userId);
    153 
    154         // explicitly set the package for security
    155         intent.setPackage(getSettingsPackageForIntent(intent));
    156 
    157         return intent;
    158     }
    159 
    160     /**
    161      * Get an intent to prompt the user to confirm credentials (pin, pattern or password)
    162      * for the previous owner of the device. The caller is expected to launch this activity using
    163      * {@link android.app.Activity#startActivityForResult(Intent, int)} and check for
    164      * {@link android.app.Activity#RESULT_OK} if the user successfully completes the challenge.
    165      *
    166      * @param alternateButtonLabel if not empty, a button is provided with the given label. Upon
    167      *                             clicking this button, the activity returns
    168      *                             {@link #RESULT_ALTERNATE}
    169      *
    170      * @return the intent for launching the activity or null if the previous owner of the device
    171      *         did not set a credential.
    172      * @throws UnsupportedOperationException if the device does not support factory reset
    173      *                                       credentials
    174      * @throws IllegalStateException if the device has already been provisioned
    175      * @hide
    176      */
    177     @SystemApi
    178     public Intent createConfirmFactoryResetCredentialIntent(
    179             CharSequence title, CharSequence description, CharSequence alternateButtonLabel) {
    180         if (!LockPatternUtils.frpCredentialEnabled(mContext)) {
    181             Log.w(TAG, "Factory reset credentials not supported.");
    182             throw new UnsupportedOperationException("not supported on this device");
    183         }
    184 
    185         // Cannot verify credential if the device is provisioned
    186         if (Settings.Global.getInt(mContext.getContentResolver(),
    187                 Settings.Global.DEVICE_PROVISIONED, 0) != 0) {
    188             Log.e(TAG, "Factory reset credential cannot be verified after provisioning.");
    189             throw new IllegalStateException("must not be provisioned yet");
    190         }
    191 
    192         // Make sure we have a credential
    193         try {
    194             IPersistentDataBlockService pdb = IPersistentDataBlockService.Stub.asInterface(
    195                     ServiceManager.getService(Context.PERSISTENT_DATA_BLOCK_SERVICE));
    196             if (pdb == null) {
    197                 Log.e(TAG, "No persistent data block service");
    198                 throw new UnsupportedOperationException("not supported on this device");
    199             }
    200             // The following will throw an UnsupportedOperationException if the device does not
    201             // support factory reset credentials (or something went wrong retrieving it).
    202             if (!pdb.hasFrpCredentialHandle()) {
    203                 Log.i(TAG, "The persistent data block does not have a factory reset credential.");
    204                 return null;
    205             }
    206         } catch (RemoteException e) {
    207             throw e.rethrowFromSystemServer();
    208         }
    209 
    210         Intent intent = new Intent(ACTION_CONFIRM_FRP_CREDENTIAL);
    211         intent.putExtra(EXTRA_TITLE, title);
    212         intent.putExtra(EXTRA_DESCRIPTION, description);
    213         intent.putExtra(EXTRA_ALTERNATE_BUTTON_LABEL, alternateButtonLabel);
    214 
    215         // explicitly set the package for security
    216         intent.setPackage(getSettingsPackageForIntent(intent));
    217 
    218         return intent;
    219     }
    220 
    221     private String getSettingsPackageForIntent(Intent intent) {
    222         List<ResolveInfo> resolveInfos = mContext.getPackageManager()
    223                 .queryIntentActivities(intent, PackageManager.MATCH_SYSTEM_ONLY);
    224         for (int i = 0; i < resolveInfos.size(); i++) {
    225             return resolveInfos.get(i).activityInfo.packageName;
    226         }
    227 
    228         return "com.android.settings";
    229     }
    230 
    231     /**
    232      * @deprecated Use {@link LayoutParams#FLAG_DISMISS_KEYGUARD}
    233      * and/or {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED}
    234      * instead; this allows you to seamlessly hide the keyguard as your application
    235      * moves in and out of the foreground and does not require that any special
    236      * permissions be requested.
    237      *
    238      * Handle returned by {@link KeyguardManager#newKeyguardLock} that allows
    239      * you to disable / reenable the keyguard.
    240      */
    241     @Deprecated
    242     public class KeyguardLock {
    243         private final IBinder mToken = new Binder();
    244         private final String mTag;
    245 
    246         KeyguardLock(String tag) {
    247             mTag = tag;
    248         }
    249 
    250         /**
    251          * Disable the keyguard from showing.  If the keyguard is currently
    252          * showing, hide it.  The keyguard will be prevented from showing again
    253          * until {@link #reenableKeyguard()} is called.
    254          *
    255          * A good place to call this is from {@link android.app.Activity#onResume()}
    256          *
    257          * Note: This call has no effect while any {@link android.app.admin.DevicePolicyManager}
    258          * is enabled that requires a password.
    259          *
    260          * @see #reenableKeyguard()
    261          */
    262         @RequiresPermission(Manifest.permission.DISABLE_KEYGUARD)
    263         public void disableKeyguard() {
    264             try {
    265                 mWM.disableKeyguard(mToken, mTag);
    266             } catch (RemoteException ex) {
    267             }
    268         }
    269 
    270         /**
    271          * Reenable the keyguard.  The keyguard will reappear if the previous
    272          * call to {@link #disableKeyguard()} caused it to be hidden.
    273          *
    274          * A good place to call this is from {@link android.app.Activity#onPause()}
    275          *
    276          * Note: This call has no effect while any {@link android.app.admin.DevicePolicyManager}
    277          * is enabled that requires a password.
    278          *
    279          * @see #disableKeyguard()
    280          */
    281         @RequiresPermission(Manifest.permission.DISABLE_KEYGUARD)
    282         public void reenableKeyguard() {
    283             try {
    284                 mWM.reenableKeyguard(mToken);
    285             } catch (RemoteException ex) {
    286             }
    287         }
    288     }
    289 
    290     /**
    291      * @deprecated Use {@link KeyguardDismissCallback}
    292      * Callback passed to {@link KeyguardManager#exitKeyguardSecurely} to notify
    293      * caller of result.
    294      */
    295     @Deprecated
    296     public interface OnKeyguardExitResult {
    297 
    298         /**
    299          * @param success True if the user was able to authenticate, false if
    300          *   not.
    301          */
    302         void onKeyguardExitResult(boolean success);
    303     }
    304 
    305     /**
    306      * Callback passed to
    307      * {@link KeyguardManager#requestDismissKeyguard(Activity, KeyguardDismissCallback)}
    308      * to notify caller of result.
    309      */
    310     public static abstract class KeyguardDismissCallback {
    311 
    312         /**
    313          * Called when dismissing Keyguard is currently not feasible, i.e. when Keyguard is not
    314          * available, not showing or when the activity requesting the Keyguard dismissal isn't
    315          * showing or isn't showing behind Keyguard.
    316          */
    317         public void onDismissError() { }
    318 
    319         /**
    320          * Called when dismissing Keyguard has succeeded and the device is now unlocked.
    321          */
    322         public void onDismissSucceeded() { }
    323 
    324         /**
    325          * Called when dismissing Keyguard has been cancelled, i.e. when the user cancelled the
    326          * operation or the bouncer was hidden for some other reason.
    327          */
    328         public void onDismissCancelled() { }
    329     }
    330 
    331     KeyguardManager(Context context) throws ServiceNotFoundException {
    332         mContext = context;
    333         mWM = WindowManagerGlobal.getWindowManagerService();
    334         mAm = ActivityManager.getService();
    335         mTrustManager = ITrustManager.Stub.asInterface(
    336                 ServiceManager.getServiceOrThrow(Context.TRUST_SERVICE));
    337     }
    338 
    339     /**
    340      * @deprecated Use {@link LayoutParams#FLAG_DISMISS_KEYGUARD}
    341      * and/or {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED}
    342      * instead; this allows you to seamlessly hide the keyguard as your application
    343      * moves in and out of the foreground and does not require that any special
    344      * permissions be requested.
    345      *
    346      * Enables you to lock or unlock the keyboard. Get an instance of this class by
    347      * calling {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}.
    348      * This class is wrapped by {@link android.app.KeyguardManager KeyguardManager}.
    349      * @param tag A tag that informally identifies who you are (for debugging who
    350      *   is disabling he keyguard).
    351      *
    352      * @return A {@link KeyguardLock} handle to use to disable and reenable the
    353      *   keyguard.
    354      */
    355     @Deprecated
    356     public KeyguardLock newKeyguardLock(String tag) {
    357         return new KeyguardLock(tag);
    358     }
    359 
    360     /**
    361      * Return whether the keyguard is currently locked.
    362      *
    363      * @return true if keyguard is locked.
    364      */
    365     public boolean isKeyguardLocked() {
    366         try {
    367             return mWM.isKeyguardLocked();
    368         } catch (RemoteException ex) {
    369             return false;
    370         }
    371     }
    372 
    373     /**
    374      * Return whether the keyguard is secured by a PIN, pattern or password or a SIM card
    375      * is currently locked.
    376      *
    377      * <p>See also {@link #isDeviceSecure()} which ignores SIM locked states.
    378      *
    379      * @return true if a PIN, pattern or password is set or a SIM card is locked.
    380      */
    381     public boolean isKeyguardSecure() {
    382         try {
    383             return mWM.isKeyguardSecure();
    384         } catch (RemoteException ex) {
    385             return false;
    386         }
    387     }
    388 
    389     /**
    390      * @deprecated Use {@link #isKeyguardLocked()} instead.
    391      *
    392      * If keyguard screen is showing or in restricted key input mode (i.e. in
    393      * keyguard password emergency screen). When in such mode, certain keys,
    394      * such as the Home key and the right soft keys, don't work.
    395      *
    396      * @return true if in keyguard restricted input mode.
    397      */
    398     public boolean inKeyguardRestrictedInputMode() {
    399         return isKeyguardLocked();
    400     }
    401 
    402     /**
    403      * Returns whether the device is currently locked and requires a PIN, pattern or
    404      * password to unlock.
    405      *
    406      * @return true if unlocking the device currently requires a PIN, pattern or
    407      * password.
    408      */
    409     public boolean isDeviceLocked() {
    410         return isDeviceLocked(mContext.getUserId());
    411     }
    412 
    413     /**
    414      * Per-user version of {@link #isDeviceLocked()}.
    415      *
    416      * @hide
    417      */
    418     public boolean isDeviceLocked(int userId) {
    419         try {
    420             return mTrustManager.isDeviceLocked(userId);
    421         } catch (RemoteException e) {
    422             return false;
    423         }
    424     }
    425 
    426     /**
    427      * Returns whether the device is secured with a PIN, pattern or
    428      * password.
    429      *
    430      * <p>See also {@link #isKeyguardSecure} which treats SIM locked states as secure.
    431      *
    432      * @return true if a PIN, pattern or password was set.
    433      */
    434     public boolean isDeviceSecure() {
    435         return isDeviceSecure(mContext.getUserId());
    436     }
    437 
    438     /**
    439      * Per-user version of {@link #isDeviceSecure()}.
    440      *
    441      * @hide
    442      */
    443     public boolean isDeviceSecure(int userId) {
    444         try {
    445             return mTrustManager.isDeviceSecure(userId);
    446         } catch (RemoteException e) {
    447             return false;
    448         }
    449     }
    450 
    451     /** @removed */
    452     @Deprecated
    453     public void dismissKeyguard(@NonNull Activity activity,
    454             @Nullable KeyguardDismissCallback callback, @Nullable Handler handler) {
    455         requestDismissKeyguard(activity, callback);
    456     }
    457 
    458     /**
    459      * If the device is currently locked (see {@link #isKeyguardLocked()}, requests the Keyguard to
    460      * be dismissed.
    461      * <p>
    462      * If the Keyguard is not secure or the device is currently in a trusted state, calling this
    463      * method will immediately dismiss the Keyguard without any user interaction.
    464      * <p>
    465      * If the Keyguard is secure and the device is not in a trusted state, this will bring up the
    466      * UI so the user can enter their credentials.
    467      * <p>
    468      * If the value set for the {@link Activity} attr {@link android.R.attr#turnScreenOn} is true,
    469      * the screen will turn on when the keyguard is dismissed.
    470      *
    471      * @param activity The activity requesting the dismissal. The activity must be either visible
    472      *                 by using {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED} or must be in a state in
    473      *                 which it would be visible if Keyguard would not be hiding it. If that's not
    474      *                 the case, the request will fail immediately and
    475      *                 {@link KeyguardDismissCallback#onDismissError} will be invoked.
    476      * @param callback The callback to be called if the request to dismiss Keyguard was successful
    477      *                 or {@code null} if the caller isn't interested in knowing the result. The
    478      *                 callback will not be invoked if the activity was destroyed before the
    479      *                 callback was received.
    480      */
    481     public void requestDismissKeyguard(@NonNull Activity activity,
    482             @Nullable KeyguardDismissCallback callback) {
    483         requestDismissKeyguard(activity, null /* message */, callback);
    484     }
    485 
    486     /**
    487      * If the device is currently locked (see {@link #isKeyguardLocked()}, requests the Keyguard to
    488      * be dismissed.
    489      * <p>
    490      * If the Keyguard is not secure or the device is currently in a trusted state, calling this
    491      * method will immediately dismiss the Keyguard without any user interaction.
    492      * <p>
    493      * If the Keyguard is secure and the device is not in a trusted state, this will bring up the
    494      * UI so the user can enter their credentials.
    495      * <p>
    496      * If the value set for the {@link Activity} attr {@link android.R.attr#turnScreenOn} is true,
    497      * the screen will turn on when the keyguard is dismissed.
    498      *
    499      * @param activity The activity requesting the dismissal. The activity must be either visible
    500      *                 by using {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED} or must be in a state in
    501      *                 which it would be visible if Keyguard would not be hiding it. If that's not
    502      *                 the case, the request will fail immediately and
    503      *                 {@link KeyguardDismissCallback#onDismissError} will be invoked.
    504      * @param message  A message that will be shown in the keyguard explaining why the user
    505      *                 would want to dismiss it.
    506      * @param callback The callback to be called if the request to dismiss Keyguard was successful
    507      *                 or {@code null} if the caller isn't interested in knowing the result. The
    508      *                 callback will not be invoked if the activity was destroyed before the
    509      *                 callback was received.
    510      * @hide
    511      */
    512     @RequiresPermission(Manifest.permission.SHOW_KEYGUARD_MESSAGE)
    513     @SystemApi
    514     public void requestDismissKeyguard(@NonNull Activity activity, @Nullable CharSequence message,
    515             @Nullable KeyguardDismissCallback callback) {
    516         try {
    517             mAm.dismissKeyguard(activity.getActivityToken(), new IKeyguardDismissCallback.Stub() {
    518                 @Override
    519                 public void onDismissError() throws RemoteException {
    520                     if (callback != null && !activity.isDestroyed()) {
    521                         activity.mHandler.post(callback::onDismissError);
    522                     }
    523                 }
    524 
    525                 @Override
    526                 public void onDismissSucceeded() throws RemoteException {
    527                     if (callback != null && !activity.isDestroyed()) {
    528                         activity.mHandler.post(callback::onDismissSucceeded);
    529                     }
    530                 }
    531 
    532                 @Override
    533                 public void onDismissCancelled() throws RemoteException {
    534                     if (callback != null && !activity.isDestroyed()) {
    535                         activity.mHandler.post(callback::onDismissCancelled);
    536                     }
    537                 }
    538             }, message);
    539         } catch (RemoteException e) {
    540             throw e.rethrowFromSystemServer();
    541         }
    542     }
    543 
    544     /**
    545      * @deprecated Use {@link LayoutParams#FLAG_DISMISS_KEYGUARD}
    546      * and/or {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED}
    547      * instead; this allows you to seamlessly hide the keyguard as your application
    548      * moves in and out of the foreground and does not require that any special
    549      * permissions be requested.
    550      *
    551      * Exit the keyguard securely.  The use case for this api is that, after
    552      * disabling the keyguard, your app, which was granted permission to
    553      * disable the keyguard and show a limited amount of information deemed
    554      * safe without the user getting past the keyguard, needs to navigate to
    555      * something that is not safe to view without getting past the keyguard.
    556      *
    557      * This will, if the keyguard is secure, bring up the unlock screen of
    558      * the keyguard.
    559      *
    560      * @param callback Let's you know whether the operation was succesful and
    561      *   it is safe to launch anything that would normally be considered safe
    562      *   once the user has gotten past the keyguard.
    563      */
    564     @Deprecated
    565     @RequiresPermission(Manifest.permission.DISABLE_KEYGUARD)
    566     public void exitKeyguardSecurely(final OnKeyguardExitResult callback) {
    567         try {
    568             mWM.exitKeyguardSecurely(new IOnKeyguardExitResult.Stub() {
    569                 public void onKeyguardExitResult(boolean success) throws RemoteException {
    570                     if (callback != null) {
    571                         callback.onKeyguardExitResult(success);
    572                     }
    573                 }
    574             });
    575         } catch (RemoteException e) {
    576 
    577         }
    578     }
    579 }
    580