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.SystemService;
24import android.app.trust.ITrustManager;
25import android.content.Context;
26import android.content.Intent;
27import android.content.pm.PackageManager;
28import android.content.pm.ResolveInfo;
29import android.os.Binder;
30import android.os.Handler;
31import android.os.IBinder;
32import android.os.Looper;
33import android.os.RemoteException;
34import android.os.ServiceManager;
35import android.os.ServiceManager.ServiceNotFoundException;
36import android.os.UserHandle;
37import android.util.Log;
38import android.view.IOnKeyguardExitResult;
39import android.view.IWindowManager;
40import android.view.WindowManager.LayoutParams;
41import android.view.WindowManagerGlobal;
42
43import com.android.internal.policy.IKeyguardDismissCallback;
44
45import java.util.List;
46
47/**
48 * Class that can be used to lock and unlock the keyboard. The
49 * actual class to control the keyboard locking is
50 * {@link android.app.KeyguardManager.KeyguardLock}.
51 */
52@SystemService(Context.KEYGUARD_SERVICE)
53public class KeyguardManager {
54
55    private static final String TAG = "KeyguardManager";
56
57    private final Context mContext;
58    private final IWindowManager mWM;
59    private final IActivityManager mAm;
60    private final ITrustManager mTrustManager;
61
62    /**
63     * Intent used to prompt user for device credentials.
64     * @hide
65     */
66    public static final String ACTION_CONFIRM_DEVICE_CREDENTIAL =
67            "android.app.action.CONFIRM_DEVICE_CREDENTIAL";
68
69    /**
70     * Intent used to prompt user for device credentials.
71     * @hide
72     */
73    public static final String ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER =
74            "android.app.action.CONFIRM_DEVICE_CREDENTIAL_WITH_USER";
75
76    /**
77     * A CharSequence dialog title to show to the user when used with a
78     * {@link #ACTION_CONFIRM_DEVICE_CREDENTIAL}.
79     * @hide
80     */
81    public static final String EXTRA_TITLE = "android.app.extra.TITLE";
82
83    /**
84     * A CharSequence description to show to the user when used with
85     * {@link #ACTION_CONFIRM_DEVICE_CREDENTIAL}.
86     * @hide
87     */
88    public static final String EXTRA_DESCRIPTION = "android.app.extra.DESCRIPTION";
89
90    /**
91     * Get an intent to prompt the user to confirm credentials (pin, pattern or password)
92     * for the current user of the device. The caller is expected to launch this activity using
93     * {@link android.app.Activity#startActivityForResult(Intent, int)} and check for
94     * {@link android.app.Activity#RESULT_OK} if the user successfully completes the challenge.
95     *
96     * @return the intent for launching the activity or null if no password is required.
97     **/
98    public Intent createConfirmDeviceCredentialIntent(CharSequence title, CharSequence description) {
99        if (!isDeviceSecure()) return null;
100        Intent intent = new Intent(ACTION_CONFIRM_DEVICE_CREDENTIAL);
101        intent.putExtra(EXTRA_TITLE, title);
102        intent.putExtra(EXTRA_DESCRIPTION, description);
103
104        // explicitly set the package for security
105        intent.setPackage(getSettingsPackageForIntent(intent));
106        return intent;
107    }
108
109    /**
110     * Get an intent to prompt the user to confirm credentials (pin, pattern or password)
111     * for the given user. The caller is expected to launch this activity using
112     * {@link android.app.Activity#startActivityForResult(Intent, int)} and check for
113     * {@link android.app.Activity#RESULT_OK} if the user successfully completes the challenge.
114     *
115     * @return the intent for launching the activity or null if no password is required.
116     *
117     * @hide
118     */
119    public Intent createConfirmDeviceCredentialIntent(
120            CharSequence title, CharSequence description, int userId) {
121        if (!isDeviceSecure(userId)) return null;
122        Intent intent = new Intent(ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER);
123        intent.putExtra(EXTRA_TITLE, title);
124        intent.putExtra(EXTRA_DESCRIPTION, description);
125        intent.putExtra(Intent.EXTRA_USER_ID, userId);
126
127        // explicitly set the package for security
128        intent.setPackage(getSettingsPackageForIntent(intent));
129
130        return intent;
131    }
132
133    private String getSettingsPackageForIntent(Intent intent) {
134        List<ResolveInfo> resolveInfos = mContext.getPackageManager()
135                .queryIntentActivities(intent, PackageManager.MATCH_SYSTEM_ONLY);
136        for (int i = 0; i < resolveInfos.size(); i++) {
137            return resolveInfos.get(i).activityInfo.packageName;
138        }
139
140        return "com.android.settings";
141    }
142
143    /**
144     * @deprecated Use {@link LayoutParams#FLAG_DISMISS_KEYGUARD}
145     * and/or {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED}
146     * instead; this allows you to seamlessly hide the keyguard as your application
147     * moves in and out of the foreground and does not require that any special
148     * permissions be requested.
149     *
150     * Handle returned by {@link KeyguardManager#newKeyguardLock} that allows
151     * you to disable / reenable the keyguard.
152     */
153    @Deprecated
154    public class KeyguardLock {
155        private final IBinder mToken = new Binder();
156        private final String mTag;
157
158        KeyguardLock(String tag) {
159            mTag = tag;
160        }
161
162        /**
163         * Disable the keyguard from showing.  If the keyguard is currently
164         * showing, hide it.  The keyguard will be prevented from showing again
165         * until {@link #reenableKeyguard()} is called.
166         *
167         * A good place to call this is from {@link android.app.Activity#onResume()}
168         *
169         * Note: This call has no effect while any {@link android.app.admin.DevicePolicyManager}
170         * is enabled that requires a password.
171         *
172         * @see #reenableKeyguard()
173         */
174        @RequiresPermission(Manifest.permission.DISABLE_KEYGUARD)
175        public void disableKeyguard() {
176            try {
177                mWM.disableKeyguard(mToken, mTag);
178            } catch (RemoteException ex) {
179            }
180        }
181
182        /**
183         * Reenable the keyguard.  The keyguard will reappear if the previous
184         * call to {@link #disableKeyguard()} caused it to be hidden.
185         *
186         * A good place to call this is from {@link android.app.Activity#onPause()}
187         *
188         * Note: This call has no effect while any {@link android.app.admin.DevicePolicyManager}
189         * is enabled that requires a password.
190         *
191         * @see #disableKeyguard()
192         */
193        @RequiresPermission(Manifest.permission.DISABLE_KEYGUARD)
194        public void reenableKeyguard() {
195            try {
196                mWM.reenableKeyguard(mToken);
197            } catch (RemoteException ex) {
198            }
199        }
200    }
201
202    /**
203     * @deprecated Use {@link KeyguardDismissCallback}
204     * Callback passed to {@link KeyguardManager#exitKeyguardSecurely} to notify
205     * caller of result.
206     */
207    @Deprecated
208    public interface OnKeyguardExitResult {
209
210        /**
211         * @param success True if the user was able to authenticate, false if
212         *   not.
213         */
214        void onKeyguardExitResult(boolean success);
215    }
216
217    /**
218     * Callback passed to {@link KeyguardManager#dismissKeyguard} to notify caller of result.
219     */
220    public static abstract class KeyguardDismissCallback {
221
222        /**
223         * Called when dismissing Keyguard is currently not feasible, i.e. when Keyguard is not
224         * available, not showing or when the activity requesting the Keyguard dismissal isn't
225         * showing or isn't showing behind Keyguard.
226         */
227        public void onDismissError() { }
228
229        /**
230         * Called when dismissing Keyguard has succeeded and the device is now unlocked.
231         */
232        public void onDismissSucceeded() { }
233
234        /**
235         * Called when dismissing Keyguard has been cancelled, i.e. when the user cancelled the
236         * operation or the bouncer was hidden for some other reason.
237         */
238        public void onDismissCancelled() { }
239    }
240
241    KeyguardManager(Context context) throws ServiceNotFoundException {
242        mContext = context;
243        mWM = WindowManagerGlobal.getWindowManagerService();
244        mAm = ActivityManager.getService();
245        mTrustManager = ITrustManager.Stub.asInterface(
246                ServiceManager.getServiceOrThrow(Context.TRUST_SERVICE));
247    }
248
249    /**
250     * @deprecated Use {@link LayoutParams#FLAG_DISMISS_KEYGUARD}
251     * and/or {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED}
252     * instead; this allows you to seamlessly hide the keyguard as your application
253     * moves in and out of the foreground and does not require that any special
254     * permissions be requested.
255     *
256     * Enables you to lock or unlock the keyboard. Get an instance of this class by
257     * calling {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}.
258     * This class is wrapped by {@link android.app.KeyguardManager KeyguardManager}.
259     * @param tag A tag that informally identifies who you are (for debugging who
260     *   is disabling he keyguard).
261     *
262     * @return A {@link KeyguardLock} handle to use to disable and reenable the
263     *   keyguard.
264     */
265    @Deprecated
266    public KeyguardLock newKeyguardLock(String tag) {
267        return new KeyguardLock(tag);
268    }
269
270    /**
271     * Return whether the keyguard is currently locked.
272     *
273     * @return true if keyguard is locked.
274     */
275    public boolean isKeyguardLocked() {
276        try {
277            return mWM.isKeyguardLocked();
278        } catch (RemoteException ex) {
279            return false;
280        }
281    }
282
283    /**
284     * Return whether the keyguard is secured by a PIN, pattern or password or a SIM card
285     * is currently locked.
286     *
287     * <p>See also {@link #isDeviceSecure()} which ignores SIM locked states.
288     *
289     * @return true if a PIN, pattern or password is set or a SIM card is locked.
290     */
291    public boolean isKeyguardSecure() {
292        try {
293            return mWM.isKeyguardSecure();
294        } catch (RemoteException ex) {
295            return false;
296        }
297    }
298
299    /**
300     * If keyguard screen is showing or in restricted key input mode (i.e. in
301     * keyguard password emergency screen). When in such mode, certain keys,
302     * such as the Home key and the right soft keys, don't work.
303     *
304     * @return true if in keyguard restricted input mode.
305     *
306     * @see android.view.WindowManagerPolicy#inKeyguardRestrictedKeyInputMode
307     */
308    public boolean inKeyguardRestrictedInputMode() {
309        try {
310            return mWM.inKeyguardRestrictedInputMode();
311        } catch (RemoteException ex) {
312            return false;
313        }
314    }
315
316    /**
317     * Returns whether the device is currently locked and requires a PIN, pattern or
318     * password to unlock.
319     *
320     * @return true if unlocking the device currently requires a PIN, pattern or
321     * password.
322     */
323    public boolean isDeviceLocked() {
324        return isDeviceLocked(UserHandle.myUserId());
325    }
326
327    /**
328     * Per-user version of {@link #isDeviceLocked()}.
329     *
330     * @hide
331     */
332    public boolean isDeviceLocked(int userId) {
333        try {
334            return mTrustManager.isDeviceLocked(userId);
335        } catch (RemoteException e) {
336            return false;
337        }
338    }
339
340    /**
341     * Returns whether the device is secured with a PIN, pattern or
342     * password.
343     *
344     * <p>See also {@link #isKeyguardSecure} which treats SIM locked states as secure.
345     *
346     * @return true if a PIN, pattern or password was set.
347     */
348    public boolean isDeviceSecure() {
349        return isDeviceSecure(UserHandle.myUserId());
350    }
351
352    /**
353     * Per-user version of {@link #isDeviceSecure()}.
354     *
355     * @hide
356     */
357    public boolean isDeviceSecure(int userId) {
358        try {
359            return mTrustManager.isDeviceSecure(userId);
360        } catch (RemoteException e) {
361            return false;
362        }
363    }
364
365    /** @removed */
366    @Deprecated
367    public void dismissKeyguard(@NonNull Activity activity,
368            @Nullable KeyguardDismissCallback callback, @Nullable Handler handler) {
369        requestDismissKeyguard(activity, callback);
370    }
371
372    /**
373     * If the device is currently locked (see {@link #isKeyguardLocked()}, requests the Keyguard to
374     * be dismissed.
375     * <p>
376     * If the Keyguard is not secure or the device is currently in a trusted state, calling this
377     * method will immediately dismiss the Keyguard without any user interaction.
378     * <p>
379     * If the Keyguard is secure and the device is not in a trusted state, this will bring up the
380     * UI so the user can enter their credentials.
381     *
382     * @param activity The activity requesting the dismissal. The activity must be either visible
383     *                 by using {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED} or must be in a state in
384     *                 which it would be visible if Keyguard would not be hiding it. If that's not
385     *                 the case, the request will fail immediately and
386     *                 {@link KeyguardDismissCallback#onDismissError} will be invoked.
387     * @param callback The callback to be called if the request to dismiss Keyguard was successful
388     *                 or {@code null} if the caller isn't interested in knowing the result. The
389     *                 callback will not be invoked if the activity was destroyed before the
390     *                 callback was received.
391     */
392    public void requestDismissKeyguard(@NonNull Activity activity,
393            @Nullable KeyguardDismissCallback callback) {
394        try {
395            mAm.dismissKeyguard(activity.getActivityToken(), new IKeyguardDismissCallback.Stub() {
396                @Override
397                public void onDismissError() throws RemoteException {
398                    if (callback != null && !activity.isDestroyed()) {
399                        activity.mHandler.post(callback::onDismissError);
400                    }
401                }
402
403                @Override
404                public void onDismissSucceeded() throws RemoteException {
405                    if (callback != null && !activity.isDestroyed()) {
406                        activity.mHandler.post(callback::onDismissSucceeded);
407                    }
408                }
409
410                @Override
411                public void onDismissCancelled() throws RemoteException {
412                    if (callback != null && !activity.isDestroyed()) {
413                        activity.mHandler.post(callback::onDismissCancelled);
414                    }
415                }
416            });
417        } catch (RemoteException e) {
418            Log.i(TAG, "Failed to dismiss keyguard: " + e);
419        }
420    }
421
422    /**
423     * @deprecated Use {@link LayoutParams#FLAG_DISMISS_KEYGUARD}
424     * and/or {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED}
425     * instead; this allows you to seamlessly hide the keyguard as your application
426     * moves in and out of the foreground and does not require that any special
427     * permissions be requested.
428     *
429     * Exit the keyguard securely.  The use case for this api is that, after
430     * disabling the keyguard, your app, which was granted permission to
431     * disable the keyguard and show a limited amount of information deemed
432     * safe without the user getting past the keyguard, needs to navigate to
433     * something that is not safe to view without getting past the keyguard.
434     *
435     * This will, if the keyguard is secure, bring up the unlock screen of
436     * the keyguard.
437     *
438     * @param callback Let's you know whether the operation was succesful and
439     *   it is safe to launch anything that would normally be considered safe
440     *   once the user has gotten past the keyguard.
441     */
442    @Deprecated
443    @RequiresPermission(Manifest.permission.DISABLE_KEYGUARD)
444    public void exitKeyguardSecurely(final OnKeyguardExitResult callback) {
445        try {
446            mWM.exitKeyguardSecurely(new IOnKeyguardExitResult.Stub() {
447                public void onKeyguardExitResult(boolean success) throws RemoteException {
448                    if (callback != null) {
449                        callback.onKeyguardExitResult(success);
450                    }
451                }
452            });
453        } catch (RemoteException e) {
454
455        }
456    }
457}
458