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
17package android.app;
18
19import android.Manifest;
20import android.annotation.NonNull;
21import android.annotation.Nullable;
22import android.annotation.RequiresPermission;
23import android.annotation.SystemApi;
24import android.annotation.SystemService;
25import android.app.trust.ITrustManager;
26import android.content.Context;
27import android.content.Intent;
28import android.content.pm.PackageManager;
29import android.content.pm.ResolveInfo;
30import android.os.Binder;
31import android.os.Handler;
32import android.os.IBinder;
33import android.os.RemoteException;
34import android.os.ServiceManager;
35import android.os.ServiceManager.ServiceNotFoundException;
36import android.os.UserHandle;
37import android.provider.Settings;
38import android.service.persistentdata.IPersistentDataBlockService;
39import android.util.Log;
40import android.view.IOnKeyguardExitResult;
41import android.view.IWindowManager;
42import android.view.WindowManager.LayoutParams;
43import android.view.WindowManagerGlobal;
44
45import com.android.internal.policy.IKeyguardDismissCallback;
46import com.android.internal.widget.LockPatternUtils;
47
48import 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)
56public 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